" Test import/export of the Vim9 script language.
" Also the autoload mechanism.

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

let s:export_script_lines =<< trim END
  vim9script
  var name: string = 'bob'
  def Concat(arg: string): string
    return name .. arg
  enddef
  g:result = Concat('bie')
  g:localname = name

  export const CONST = 1234
  export var exported = 9876
  export var exp_name = 'John'
  export def Exported(): string
    return 'Exported'
  enddef
  export def ExportedValue(): number
    return exported
  enddef
  export def ExportedInc()
    exported += 5
  enddef
  export final theList = [1]
  export def AddSome(s: string): string
    return s .. 'some'
  enddef
  export var AddRef = AddSome
END

def s:Undo_export_script_lines()
  unlet g:result
  unlet g:localname
enddef

def Test_vim9_import_export()
  writefile(s:export_script_lines, 'Xexport.vim', 'D')
  var import_script_lines =<< trim END
    vim9script
    var dir = './'
    var ext = ".vim"
    import dir .. 'Xexport' .. ext as expo

    g:exported1 = expo.exported
    expo.exported += 3
    g:exported2 = expo.exported
    g:exported3 = expo.ExportedValue()

    expo.ExportedInc()
    g:exported_i1 = expo.exported
    g:exported_i2 = expo.ExportedValue()

    expo.exported = 11
    g:exported_s1 = expo.exported
    g:exported_s2 = expo.ExportedValue()

    g:imported_func = expo.Exported()

    def GetExported(): string
      var local_dict = {ref: expo.Exported}
      return local_dict.ref()
    enddef
    g:funcref_result = GetExported()

    def GetName(): string
      return expo.exp_name .. 'son'
    enddef
    g:long_name = GetName()

    g:imported_name = expo.exp_name
    expo.exp_name ..= ' Doe'
    expo.exp_name = expo.exp_name .. ' Maar'
    g:imported_name_appended = expo.exp_name
    g:exported_later = expo.exported

    expo.theList->add(2)
    assert_equal([1, 2], expo.theList)

    assert_equal('andthensome', 'andthen'->expo.AddSome())
    assert_equal('awesome', 'awe'->expo.AddRef())
  END
  writefile(import_script_lines, 'Ximport.vim', 'D')
  source Ximport.vim

  assert_equal('bobbie', g:result)
  assert_equal('bob', g:localname)
  assert_equal(9876, g:exported1)
  assert_equal(9879, g:exported2)
  assert_equal(9879, g:exported3)

  assert_equal(9884, g:exported_i1)
  assert_equal(9884, g:exported_i2)

  assert_equal(11, g:exported_s1)
  assert_equal(11, g:exported_s2)
  assert_equal(11, g:exported_later)

  assert_equal('Exported', g:imported_func)
  assert_equal('Exported', g:funcref_result)
  assert_equal('John', g:imported_name)
  assert_equal('Johnson', g:long_name)
  assert_equal('John Doe Maar', g:imported_name_appended)
  assert_false(exists('g:name'))

  Undo_export_script_lines()
  unlet g:exported1
  unlet g:exported2
  unlet g:exported3
  unlet g:exported_i1
  unlet g:exported_i2
  unlet g:exported_later
  unlet g:imported_func
  unlet g:imported_name g:long_name g:imported_name_appended
  delete('Ximport.vim')

  # similar, with line breaks
  var import_line_break_script_lines =<< trim END
    vim9script
    import './Xexport.vim'
        as expo
    g:exported = expo.exported
    expo.exported += 7
    g:exported_added = expo.exported
    g:imported_func = expo.Exported()
  END
  writefile(import_line_break_script_lines, 'Ximport_lbr.vim')
  source Ximport_lbr.vim

  assert_equal(11, g:exported)
  assert_equal(18, g:exported_added)
  assert_equal('Exported', g:imported_func)

  # exported script not sourced again
  assert_false(exists('g:result'))
  unlet g:exported
  unlet g:exported_added
  unlet g:imported_func
  delete('Ximport_lbr.vim')

  var import_shadows_cmdmod_lines =<< trim END
      vim9script
      import './Xexport.vim' as vim9
      vim9.exp_name = 'Shadow'
      assert_equal('Shadow', vim9.exp_name)
  END
  v9.CheckScriptSuccess(import_shadows_cmdmod_lines)

  var line_break_before_dot =<< trim END
    vim9script
    import './Xexport.vim' as expo
    g:exported = expo
                  .exported
  END
  writefile(line_break_before_dot, 'Ximport_lbr_before_dot.vim')
  assert_fails('source Ximport_lbr_before_dot.vim', 'E1060:', '', 3)
  delete('Ximport_lbr_before_dot.vim')

  var line_break_after_dot =<< trim END
    vim9script
    import './Xexport.vim' as expo
    g:exported = expo.
                  exported
  END
  writefile(line_break_after_dot, 'Ximport_lbr_after_dot.vim')
  assert_fails('source Ximport_lbr_after_dot.vim', 'E1074:', '', 3)
  delete('Ximport_lbr_after_dot.vim')

  var import_star_as_lines =<< trim END
    vim9script
    import './Xexport.vim' as Export
    def UseExport()
      g:exported_def = Export.exported
    enddef
    g:exported_script = Export.exported
    assert_equal(1, exists('Export.exported'))
    assert_equal(0, exists('Export.notexported'))
    UseExport()
  END
  writefile(import_star_as_lines, 'Ximport.vim')
  source Ximport.vim

  assert_equal(18, g:exported_def)
  assert_equal(18, g:exported_script)
  unlet g:exported_def
  unlet g:exported_script

  var import_star_as_lines_no_dot =<< trim END
    vim9script
    import './Xexport.vim' as Export
    def Func()
      var dummy = 1
      var imported = Export + dummy
    enddef
    defcompile
  END
  writefile(import_star_as_lines_no_dot, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1060:', '', 2, 'Func')

  var import_star_as_lines_dot_space =<< trim END
    vim9script
    import './Xexport.vim' as Export
    def Func()
      var imported = Export . exported
    enddef
    defcompile
  END
  writefile(import_star_as_lines_dot_space, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1074:', '', 1, 'Func')

  writefile(s:export_script_lines, 'Xexport2.vim')
  var import_as_duplicated =<< trim END
    vim9script
    import './Xexport.vim' as expo
    import './Xexport2.vim' as expo
  END
  writefile(import_as_duplicated, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim')
  delete('Xexport2.vim')

  var import_star_as_lines_script_no_dot =<< trim END
    vim9script
    import './Xexport.vim' as Export
    g:imported_script = Export exported
  END
  writefile(import_star_as_lines_script_no_dot, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1060: Expected dot after name: Export exported')

  var import_star_as_lines_script_space_after_dot =<< trim END
    vim9script
    import './Xexport.vim' as Export
    g:imported_script = Export. exported
  END
  writefile(import_star_as_lines_script_space_after_dot, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1074:')

  var import_star_as_lines_missing_name =<< trim END
    vim9script
    import './Xexport.vim' as Export
    def Func()
      var imported = Export.
    enddef
    defcompile
  END
  writefile(import_star_as_lines_missing_name, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1048:', '', 1, 'Func')

  var import_star_as_lbr_lines =<< trim END
    vim9script
    import './Xexport.vim'
        as Export
    def UseExport()
      g:exported = Export.exported
    enddef
    UseExport()
  END
  writefile(import_star_as_lbr_lines, 'Ximport.vim')
  source Ximport.vim
  assert_equal(18, g:exported)
  unlet g:exported

  # try to use something that exists but is not exported
  var import_not_exported_lines =<< trim END
    vim9script
    import './Xexport.vim' as expo
    echo expo.name
  END
  writefile(import_not_exported_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1049:', '', 3, 'Ximport.vim')

  # try to import something that is already defined
  var import_already_defined =<< trim END
    vim9script
    var exported = 'something'
    import './Xexport.vim' as exported
  END
  writefile(import_already_defined, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim')

  # try changing an imported const
  var import_assign_to_const =<< trim END
    vim9script
    import './Xexport.vim' as expo
    def Assign()
      expo.CONST = 987
    enddef
    defcompile
  END
  writefile(import_assign_to_const, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E46:', '', 1, '_Assign')

  # try changing an imported final
  var import_assign_to_final =<< trim END
    vim9script
    import './Xexport.vim' as expo
    def Assign()
      expo.theList = [2]
    enddef
    defcompile
  END
  writefile(import_assign_to_final, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E46:', '', 1, '_Assign')

  var import_no_as_lines =<< trim END
    vim9script
    import './Xexport.vim' name
  END
  writefile(import_no_as_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E488:', '', 2, 'Ximport.vim')

  # trailing starts with "as"
  var import_bad_as_lines =<< trim END
    vim9script
    import './Xexport.vim' asname
  END
  writefile(import_no_as_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E488:', '', 2, 'Ximport.vim')

  var import_invalid_string_lines =<< trim END
    vim9script
    import Xexport.vim
  END
  writefile(import_invalid_string_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E121:', '', 2, 'Ximport.vim')

  var import_wrong_name_lines =<< trim END
    vim9script
    import './XnoExport.vim'
  END
  writefile(import_wrong_name_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1053:', '', 2, 'Ximport.vim')

  var import_redefining_lines =<< trim END
    vim9script
    import './Xexport.vim' as exported
    var exported = 5
  END
  writefile(import_redefining_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1213: Redefining imported item "exported"', '', 3)

  var import_missing_dot_lines =<< trim END
    vim9script
    import './Xexport.vim' as expo
    def Test()
      expo = 9
    enddef
    defcompile
  END
  writefile(import_missing_dot_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1258:', '', 1)

  var import_missing_name_lines =<< trim END
    vim9script
    import './Xexport.vim' as expo
    def Test()
      expo.99 = 9
    enddef
    defcompile
  END
  writefile(import_missing_name_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1259:', '', 1)

  var import_assign_wrong_type_lines =<< trim END
    vim9script
    import './Xexport.vim' as expo
    expo.exported = 'xxx'
  END
  writefile(import_assign_wrong_type_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E1012: Type mismatch; expected number but got string', '', 3)

  var import_assign_const_lines =<< trim END
    vim9script
    import './Xexport.vim' as expo
    expo.CONST = 4321
  END
  writefile(import_assign_const_lines, 'Ximport.vim')
  assert_fails('source Ximport.vim', 'E46: Cannot change read-only variable "CONST"', '', 3)

  # Check that in a Vim9 script 'cpo' is set to the Vim default.
  # Flags added or removed are also applied to the restored value.
  set cpo=abcd
  var lines =<< trim END
    vim9script
    g:cpo_in_vim9script = &cpo
    set cpo+=f
    set cpo-=c
    g:cpo_after_vim9script = &cpo
  END
  writefile(lines, 'Xvim9_script', 'D')
  source Xvim9_script
  assert_equal('fabd', &cpo)
  set cpo&vim
  assert_equal(&cpo, g:cpo_in_vim9script)
  var newcpo = substitute(&cpo, 'c', '', '') .. 'f'
  assert_equal(newcpo, g:cpo_after_vim9script)

  delete('Xvim9_script')
enddef

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

      export var verylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongname = 'asdf'
  END
  writefile(lines, 'Xverylong.vim', 'D')

  lines =<< trim END
      vim9script
      import './Xverylong.vim'

      g:result = Xverylong.verylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongname
  END
  v9.CheckScriptSuccess(lines)
  assert_equal('asdf', g:result)

  unlet g:result
enddef

def Test_import_funcref()
  var lines =<< trim END
      vim9script
      export def F(): number
        return 42
      enddef
      export const G = F
  END
  writefile(lines, 'Xlib.vim', 'D')

  lines =<< trim END
      vim9script
      import './Xlib.vim' as lib
      const Foo = lib.G()
      assert_equal(42, Foo)

      def DoTest()
        const Goo = lib.G()
        assert_equal(42, Goo)
      enddef
      DoTest()
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_export_closure()
  # tests that the closure in block can be compiled, not the import part
  var lines =<< trim END
      vim9script
      {
        var foo = 42
        export def Bar(): number
          return foo
        enddef
      }
      assert_equal(42, Bar())
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_import_duplicate_function()
  # Function Hover() exists in both scripts, partial should refer to the right
  # one.
  var lines =<< trim END
      vim9script

      def Hover(d: dict<any>): string
        return 'found it'
      enddef

      export def NewLspServer(): dict<any>
        var d: dict<any> = {}
        d->extend({hover: function('Hover', [d])})
        return d
      enddef

      NewLspServer()
  END
  writefile(lines, 'Xserver.vim', 'D')

  lines =<< trim END
      vim9script

      import './Xserver.vim' as server

      export def Hover()
      enddef

      def AddServer()
        var d: dict<any> = server.NewLspServer()
        assert_equal('found it', d.hover())
      enddef
      AddServer()
  END
  v9.CheckScriptSuccess(lines)
enddef


def Test_import_fails()
  writefile([], 'Xfoo.vim', 'D')
  var lines =<< trim END
      import './Xfoo.vim' as foo
      foo = 'bar'
  END
  v9.CheckDefAndScriptFailure(lines, ['E1094:', 'E1236: Cannot use foo itself'])
  lines =<< trim END
      vim9script
      import './Xfoo.vim' as foo
      var that = foo
  END
  v9.CheckScriptFailure(lines, 'E1060: Expected dot after name: foo')
  lines =<< trim END
      vim9script
      import './Xfoo.vim' as foo
      var that: any
      that += foo
  END
  v9.CheckScriptFailure(lines, 'E1060: Expected dot after name: foo')
  lines =<< trim END
      vim9script
      import './Xfoo.vim' as foo
      foo += 9
  END
  v9.CheckScriptFailure(lines, 'E1060: Expected dot after name: foo')

  lines =<< trim END
      vim9script
      import './Xfoo.vim' as 9foo
  END
  v9.CheckScriptFailure(lines, 'E1047:')
  lines =<< trim END
      vim9script
      import './Xfoo.vim' as the#foo
  END
  v9.CheckScriptFailure(lines, 'E1047:')
  lines =<< trim END
      vim9script
      import './Xfoo.vim' as g:foo
  END
  v9.CheckScriptFailure(lines, 'E1047:')

  lines =<< trim END
      vim9script
      def TheFunc()
        echo 'the func'
      enddef
      export var Ref = TheFunc
  END
  writefile([], 'Xthat.vim')

  lines =<< trim END
      import './Xthat.vim' as That
      That()
  END
  v9.CheckDefAndScriptFailure(lines, ['E1094:', 'E1236: Cannot use That itself'])

  lines =<< trim END
      vim9script
      import './Xthat.vim' as That
      def Func()
        echo That()
      enddef
      Func()
  END
  v9.CheckScriptFailure(lines, 'E1236: Cannot use That itself')

  lines =<< trim END
      import './Xthat.vim' as one
      import './Xthat.vim' as two
  END
  v9.CheckScriptFailure(lines, 'E1262:')

  delete('Xthat.vim')

  lines =<< trim END
      vim9script
      export var item = 'hello'
      import './Xyourself.vim'
  END
  writefile(lines, 'Xyourself.vim', 'D')
  assert_fails('source Xyourself.vim', 'E1088:')

  mkdir('Ximport', 'R')

  writefile(['vim9script'], 'Ximport/.vim')
  lines =<< trim END
      vim9script
      import './Ximport/.vim'
  END
  v9.CheckScriptFailure(lines, 'E1261: Cannot import .vim without using "as"')
  lines =<< trim END
      vim9script
      import './Ximport/.vim' as vim
  END
  v9.CheckScriptSuccess(lines)

  writefile(['vim9script'], 'Ximport/.vimrc')
  lines =<< trim END
      vim9script
      import './Ximport/.vimrc'
  END
  v9.CheckScriptFailure(lines, 'E1257: Imported script must use "as" or end in .vim')
  lines =<< trim END
      vim9script
      import './Ximport/.vimrc' as vimrc
  END
  v9.CheckScriptSuccess(lines)

  new
  setline(1, ['vim9script', 'import "" as abc'])
  assert_fails('source', 'E1071: Invalid string for :import: "" as abc')
  setline(2, 'import [] as abc')
  assert_fails('source', 'E1071: Invalid string for :import: [] as abc')
  setline(2, 'import test_null_string() as abc')
  assert_fails('source', 'E1071: Invalid string for :import: test_null_string() as abc')
  bw!
  call writefile(['vim9script', "import './Xfoo.vim' ask expo"], 'Xbar.vim')
  assert_fails('source Xbar.vim', 'E488: Trailing characters: ask expo')
  writefile([], 'Xtemp')
  call writefile(['vim9script', "import './Xtemp'"], 'Xbar.vim')
  assert_fails('source Xbar.vim', 'E1257: Imported script must use "as" or end in .vim: Xtemp')
  delete('Xtemp')
  call writefile(['vim9script', "import './Xfoo.vim' as abc | foobar"], 'Xbar.vim')
  assert_fails('source Xbar.vim', 'E492: Not an editor command:  foobar')
  call delete('Xbar.vim')
enddef

func g:Trigger()
  source Ximport.vim
  return "echo 'yes'\<CR>"
endfunc

def Test_import_export_expr_map()
  # check that :import and :export work when buffer is locked
  var export_lines =<< trim END
    vim9script
    export def That(): string
      return 'yes'
    enddef
  END
  writefile(export_lines, 'Xexport_that.vim', 'D')

  var import_lines =<< trim END
    vim9script
    import './Xexport_that.vim' as that
    assert_equal('yes', that.That())
  END
  writefile(import_lines, 'Ximport.vim', 'D')

  nnoremap <expr> trigger g:Trigger()
  feedkeys('trigger', "xt")

  nunmap trigger
enddef

def Test_import_in_filetype()
  # check that :import works when the buffer is locked
  mkdir('ftplugin', 'pR')
  var export_lines =<< trim END
    vim9script
    export var That = 'yes'
  END
  writefile(export_lines, 'ftplugin/Xexport_ft.vim')

  var import_lines =<< trim END
    vim9script
    import './Xexport_ft.vim' as ft
    assert_equal('yes', ft.That)
    g:did_load_mytpe = 1
  END
  writefile(import_lines, 'ftplugin/qf.vim')

  var save_rtp = &rtp
  &rtp = getcwd() .. ',' .. &rtp

  filetype plugin on
  copen
  assert_equal(1, g:did_load_mytpe)

  quit!
  &rtp = save_rtp
enddef

def Test_use_import_in_mapping()
  var lines =<< trim END
      vim9script
      export def Funcx(nr: number)
        g:result = nr
      enddef
  END
  writefile(lines, 'XsomeExport.vim', 'D')
  lines =<< trim END
      vim9script
      import './XsomeExport.vim' as some
      var Funcy = some.Funcx
      nnoremap <F3> :call <sid>Funcy(42)<cr>
      nnoremap <F4> :call <sid>some.Funcx(44)<cr>
  END
  writefile(lines, 'Xmapscript.vim', 'D')

  source Xmapscript.vim
  feedkeys("\<F3>", "xt")
  assert_equal(42, g:result)
  feedkeys("\<F4>", "xt")
  assert_equal(44, g:result)

  unlet g:result
  nunmap <F3>
  nunmap <F4>
enddef

def Test_use_relative_autoload_import_in_mapping()
  var lines =<< trim END
      vim9script
      export def Func()
        g:result = 42
      enddef
  END
  writefile(lines, 'XrelautoloadExport.vim', 'D')
  lines =<< trim END
      vim9script
      import autoload './XrelautoloadExport.vim' as some
      nnoremap <F3> :call <SID>some.Func()<CR>
  END
  writefile(lines, 'Xmapscript.vim', 'D')

  source Xmapscript.vim
  assert_match('\d\+ A: .*XrelautoloadExport.vim', execute('scriptnames')->split("\n")[-1])
  var l = getscriptinfo()
  assert_match('XrelautoloadExport.vim$', l[-1].name)
  assert_true(l[-1].autoload)
  feedkeys("\<F3>", "xt")
  assert_equal(42, g:result)
  l = getscriptinfo({name: 'XrelautoloadExport'})
  assert_true(len(l) == 1)
  assert_match('XrelautoloadExport.vim$', l[0].name)
  assert_false(l[0].autoload)
  assert_equal(999999, l[0].version)

  unlet g:result
  nunmap <F3>
enddef

def Test_autoload_import_var()
  # variable name starts with "autoload"
  var lines =<< trim END
      vim9script
      var autoloaded = "Xtest.vim"
      import autoloaded
  END
  v9.CheckScriptFailure(lines, 'E1053: Could not import "Xtest.vim')
enddef

def Test_use_autoload_import_in_mapping()
  var lines =<< trim END
      vim9script
      export def Func()
        g:result = 49
      enddef
  END
  mkdir('Ximpdir/autoload', 'pR')
  writefile(lines, 'Ximpdir/autoload/XautoloadExport.vim')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Ximpdir'

  lines =<< trim END
      vim9script
      import autoload 'XautoloadExport.vim' as some
      nnoremap <F3> :call <SID>some.Func()<CR>
  END
  writefile(lines, 'Xmapscript.vim', 'D')

  source Xmapscript.vim
  assert_match('\d\+ A: .*autoload/XautoloadExport.vim', execute('scriptnames')->split("\n")[-1])
  feedkeys("\<F3>", "xt")
  assert_equal(49, g:result)

  unlet g:result
  nunmap <F3>
  &rtp = save_rtp
enddef

def Test_use_import_in_command_completion()
  var lines =<< trim END
      vim9script
      export def Complete(..._): list<string>
        return ['abcd']
      enddef
  END
  writefile(lines, 'Xscript.vim', 'D')

  lines =<< trim END
      vim9script
      import './Xscript.vim'

      command -nargs=1 -complete=customlist,Xscript.Complete Cmd echo 'ok'
      feedkeys(":Cmd ab\<Tab>\<C-B>#\<CR>", 'xnt')
      assert_equal('#Cmd abcd', @:)
  END
  v9.CheckScriptSuccess(lines)

  delcommand Cmd
enddef

def Test_use_import_with_funcref_in_command_completion()
  var lines =<< trim END
      vim9script
      export def Complete(..._): list<string>
        return ['abcd']
      enddef
  END
  writefile(lines, 'Xscript.vim', 'D')

  lines =<< trim END
      vim9script
      import './Xscript.vim'

      var Ref = Xscript.Complete
      exe "command -nargs=1 -complete=customlist," .. expand('<SID>') .. "Ref  Cmd echo 'ok'"
      feedkeys(":Cmd ab\<Tab>\<C-B>#\<CR>", 'xnt')
      assert_equal('#Cmd abcd', @:)
  END
  v9.CheckScriptSuccess(lines)

  delcommand Cmd
enddef

def Test_use_autoload_import_in_insert_completion()
  mkdir('Xinsdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xinsdir'

  var lines =<< trim END
      vim9script
      export def ThesaurusFunc(findbase: bool, _): any
        if findbase
          return 1
        endif
        return [
          'check',
          'experiment',
          'test',
          'verification'
          ]
      enddef
      g:completion_loaded = 'yes'
  END
  writefile(lines, 'Xinsdir/autoload/completion.vim')

  new
  lines =<< trim END
      vim9script
      g:completion_loaded = 'no'
      import autoload 'completion.vim'
      set thesaurusfunc=completion.ThesaurusFunc
      assert_equal('no', g:completion_loaded)
      feedkeys("i\<C-X>\<C-T>\<C-N>\<Esc>", 'xt')
      assert_equal('experiment', getline(1))
      assert_equal('yes', g:completion_loaded)
  END
  v9.CheckScriptSuccess(lines)

  set thesaurusfunc=
  bwipe!
  &rtp = save_rtp
enddef

def Test_use_autoload_import_partial_in_opfunc()
  mkdir('Xpartdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xpartdir'

  var lines =<< trim END
      vim9script
      export def Opfunc1(..._)
        g:opfunc_called = 'yes'
      enddef
  END
  writefile(lines, 'Xpartdir/autoload/opfunc.vim')

  new
  lines =<< trim END
      vim9script
      import autoload 'opfunc.vim'
      nnoremap <expr> <F3> TheFunc()
      def TheFunc(): string
        &operatorfunc = function('opfunc.Opfunc1', [0])
        return 'g@'
      enddef
      feedkeys("\<F3>l", 'xt')
      assert_equal('yes', g:opfunc_called)
  END
  v9.CheckScriptSuccess(lines)

  set opfunc=
  bwipe!
  nunmap <F3>
  &rtp = save_rtp
enddef

def Test_set_opfunc_to_autoload_func_directly()
  mkdir('Xdirdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xdirdir'

  var lines =<< trim END
      vim9script
      export def Opfunc2(..._)
        g:opfunc_called = 'yes'
      enddef
  END
  writefile(lines, 'Xdirdir/autoload/opfunc.vim')

  new
  lines =<< trim END
      vim9script
      import autoload 'opfunc.vim'
      nnoremap <expr> <F3> TheFunc()
      def TheFunc(): string
        &operatorfunc = opfunc.Opfunc2
        return 'g@'
      enddef
      feedkeys("\<F3>l", 'xt')
      assert_equal('yes', g:opfunc_called)
  END
  v9.CheckScriptSuccess(lines)

  set opfunc=
  bwipe!
  nunmap <F3>
  &rtp = save_rtp
enddef

def Test_use_autoload_import_in_fold_expression()
  mkdir('Xfolddir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xfolddir'

  var lines =<< trim END
      vim9script
      export def Expr(): string
        return getline(v:lnum) =~ '^#' ? '>1' : '1'
      enddef
      export def Text(): string
        return 'fold text'
      enddef
      g:fold_loaded = 'yes'
  END
  writefile(lines, 'Xfolddir/autoload/fold.vim')

  lines =<< trim END
      vim9script
      import autoload 'fold.vim'
      &foldexpr = 'fold.Expr()'
      &foldtext = 'fold.Text()'
      &foldmethod = 'expr'
      &debug = 'throw'
  END
  new
  setline(1, ['# one', 'text', '# two', 'text'])
  g:fold_loaded = 'no'
  v9.CheckScriptSuccess(lines)
  assert_equal('no', g:fold_loaded)
  redraw
  assert_equal('yes', g:fold_loaded)

  # Check that script context of 'foldexpr' is copied to another buffer.
  edit! otherfile
  redraw

  set foldexpr= foldtext& foldmethod& debug=
  bwipe!
  &rtp = save_rtp
enddef

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

      g:loaded = 'yes'
      export def RelFunc(): string
        return 'relfunc'
      enddef
      def NotExported()
        echo 'not'
      enddef

      export var someText = 'some text'
      var notexp = 'bad'
  END
  writefile(lines, 'XimportRel.vim', 'D')
  writefile(lines, 'XimportRel2.vim', 'D')
  writefile(lines, 'XimportRel3.vim', 'D')
  writefile(lines, 'XimportRel4.vim', 'D')
  writefile(lines, 'XimportRel5.vim', 'D')

  lines =<< trim END
      vim9script
      g:loaded = 'no'
      import autoload './XimportRel.vim'
      assert_equal('no', g:loaded)

      def AFunc(): string
        var res = ''
        res ..= XimportRel.RelFunc()
        res ..= '/'
        res ..= XimportRel.someText
        XimportRel.someText = 'from AFunc'
        return res
      enddef
      # script not loaded when compiling
      defcompile
      assert_equal('no', g:loaded)

      assert_equal('relfunc/some text', AFunc())
      assert_equal('yes', g:loaded)
      unlet g:loaded

      assert_equal('from AFunc', XimportRel.someText)
      XimportRel.someText = 'from script'
      assert_equal('from script', XimportRel.someText)
  END
  v9.CheckScriptSuccess(lines)

  lines =<< trim END
      vim9script
      import autoload './XimportRel.vim'
      echo XimportRel.NotExported()
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExported', 3)

  lines =<< trim END
      vim9script
      import autoload './XimportRel.vim'
      echo XimportRel.notexp
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 3)

  lines =<< trim END
      vim9script
      import autoload './XimportRel.vim'
      XimportRel.notexp = 'bad'
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 3)

  lines =<< trim END
      vim9script
      import autoload './XimportRel.vim'
      def Func()
        echo XimportRel.NotExported()
      enddef
      Func()
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExported', 1)

  lines =<< trim END
      vim9script
      import autoload './XimportRel.vim'
      def Func()
        echo XimportRel.notexp
      enddef
      Func()
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1)

  # Same, script not imported before
  lines =<< trim END
      vim9script
      import autoload './XimportRel4.vim'
      def Func()
        echo XimportRel4.notexp
      enddef
      Func()
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1)

  # does not fail if the script wasn't loaded yet and only compiling
  g:loaded = 'no'
  lines =<< trim END
      vim9script
      import autoload './XimportRel2.vim'
      def Func()
        echo XimportRel2.notexp
      enddef
      defcompile
  END
  v9.CheckScriptSuccess(lines)
  assert_equal('no', g:loaded)

  lines =<< trim END
      vim9script
      import autoload './XimportRel.vim'
      def Func()
        XimportRel.notexp = 'bad'
      enddef
      Func()
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1)

  # fails with a not loaded import
  lines =<< trim END
      vim9script
      import autoload './XimportRel3.vim'
      def Func()
        XimportRel3.notexp = 'bad'
      enddef
      Func()
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1)
  assert_equal('yes', g:loaded)
  unlet g:loaded

  lines =<< trim END
      vim9script
      import autoload './XimportRel5.vim'
      def Func()
        XimportRel5.nosuchvar = 'bad'
      enddef
      Func()
  END
  v9.CheckScriptFailure(lines, 'E121: Undefined variable: nosuchvar', 1)
  unlet g:loaded

  # nasty: delete script after compiling function
  writefile(['vim9script'], 'XimportRelDel.vim')
  lines =<< trim END
      vim9script

      import autoload './XimportRelDel.vim'
      def DoIt()
        echo XimportRelDel.var
      enddef
      defcompile
      delete('XimportRelDel.vim')
      DoIt()
  END
  v9.CheckScriptFailure(lines, 'E484:')
enddef

" autoload relative, access from compiled function.
" Github issues: #14565, #14579
def Test_autoload_import_relative_compiled_buffer()
  var lines =<< trim END
    vim9script

    export def F1(): string
        return 'InFile.vim'
    enddef
  END
  writefile(lines, 'Ximportrelativebuffer.vim', 'D')
  lines =<< trim END
    vim9script

    import autoload './Ximportrelativebuffer.vim' as xfile

    def F(): string
      return xfile.F1()
    enddef
    assert_equal('InFile.vim', F())
  END
  new
  setline(1, lines)
  :source
  # source one more time to detect issues with clearing the script state and
  # variables
  :source
  :bw!
enddef

" Test for relative import when sourcing a buffer in another directory
def Test_autoload_import_relative_from_buffer_in_dir()
  mkdir('Ximportrelative/dir1/dir2', 'pR')
  var lines =<< trim END
    vim9script

    export def F1(): string
        return 'InFile.vim'
    enddef
  END
  writefile(lines, 'Ximportrelative/dir1/dir2/Ximport.vim')
  lines =<< trim END
    vim9script

    import autoload './Ximport.vim' as xfile

    def F(): string
      return xfile.F1()
    enddef
    assert_equal('InFile.vim', F())
  END
  writefile(lines, 'Ximportrelative/dir1/dir2/Xrelative.vim')

  split Ximportrelative/dir1/dir2/Xrelative.vim
  :source
  # source one more time to detect issues with clearing the script state and
  # variables
  :source
  :bw!
enddef

" Test modifying exported autoload variable. Github issue: #14591
def Test_autoload_export_variables()
  mkdir('Xautoload_vars/autoload', 'pR')
  var lines =<< trim END
    vim9script
    g:Xautoload_vars_autoload = true
    export var val = 11
    val = 42
  END

  # Test that the imported script, above, can modify the exported variable;
  # and test that the importing script, below, can modify the variable.
  writefile(lines, 'Xautoload_vars/autoload/Xauto_vars_f2.vim', 'D')
  lines =<< trim END
    vim9script
    g:Xautoload_vars_autoload = false

    import autoload './Xautoload_vars/autoload/Xauto_vars_f2.vim' as f2
    # Verify that the import statement does not load the file.
    assert_equal(false, g:Xautoload_vars_autoload)

    def F(): number
      return f2.val
    enddef
    # Verify compile does not load the file.
    defcompile F
    assert_equal(false, g:Xautoload_vars_autoload)

    # load the file by accessing the exported variable
    assert_equal(42, F())
    assert_equal(true, g:Xautoload_vars_autoload)
    unlet g:Xautoload_vars_autoload

    assert_equal(42, f2.val)
    f2.val = 17
    assert_equal(17, f2.val)

    def G()
      f2.val = 19
    enddef
    G()
    assert_equal(19, f2.val)
  END
  v9.CheckScriptSuccess(lines)

  # Test const var is not modifiable.
  lines =<< trim END
    vim9script
    export const val = 11
    val = 42
  END
  writefile(lines, 'Xautoload_vars/autoload/Xauto_vars_f3.vim', 'D')
  lines =<< trim END
    vim9script

    import autoload './Xautoload_vars/autoload/Xauto_vars_f3.vim' as f3

    var x = f3.val
  END
  v9.CheckScriptFailure(lines, 'E46:')

  # Test const var is not modifiable from importing script.
  lines =<< trim END
    vim9script
    export const val = 11
  END
  writefile(lines, 'Xautoload_vars/autoload/Xauto_vars_f4.vim', 'D')
  lines =<< trim END
    vim9script

    import autoload './Xautoload_vars/autoload/Xauto_vars_f4.vim' as f4

    f4.val = 13
  END
  v9.CheckScriptFailure(lines, 'E46:')

  # Test const var is not modifiable from importing script from :def.
  # Github issue: #14606
  lines =<< trim END
    vim9script
    export const val = 11
  END
  writefile(lines, 'Xautoload_vars/autoload/Xauto_vars_f5.vim', 'D')
  lines =<< trim END
    vim9script

    import autoload './Xautoload_vars/autoload/Xauto_vars_f5.vim' as f5

    def F()
      f5.val = 13
    enddef
    F()
  END
  v9.CheckScriptFailure(lines, 'E741:')

  # Still part of Github issue: #14606
  lines =<< trim END
    vim9script
    export var val = 11
  END
  writefile(lines, 'Xautoload_vars/autoload/Xauto_vars_f6.vim', 'D')
  lines =<< trim END
    vim9script

    import autoload './Xautoload_vars/autoload/Xauto_vars_f6.vim' as f6

    def F()
      f6.val = 13
    enddef
    F()
    assert_equal(13, f6.val)
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_autoload_import_relative_autoload_dir()
  mkdir('autoload', 'pR')
  var lines =<< trim END
      vim9script
      export def Bar()
        g:called_bar = 'yes'
      enddef
  END
  writefile(lines, 'autoload/script.vim')

  lines =<< trim END
      vim9script
      import autoload './autoload/script.vim'
      def Foo()
        script.Bar()
      enddef
      Foo()
      assert_equal('yes', g:called_bar)
  END
  v9.CheckScriptSuccess(lines)

  unlet g:called_bar
enddef

def Test_autoload_import_deleted()
  var lines =<< trim END
      vim9script
      export const FOO = 1
  END
  writefile(lines, 'Xa.vim', 'D')

  lines =<< trim END
      vim9script
      import autoload './Xa.vim'

      delete('Xa.vim')
      var x = Xa.FOO
  END
  v9.CheckScriptFailure(lines, 'E484:')
enddef

def Test_autoload_import_using_const()
  mkdir('Xdir/autoload', 'pR')
  var lines =<< trim END
      vim9script
      export const FOO = 42
      echomsg FOO
  END
  writefile(lines, 'Xdir/autoload/exp.vim')

  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xdir'
  lines =<< trim END
      vim9script
      import autoload 'exp.vim'
      assert_equal(42, exp.FOO)
  END
  v9.CheckScriptSuccess(lines)
  &rtp = save_rtp
enddef

func Test_import_in_diffexpr()
  CheckExecutable diff

  call Run_Test_import_in_diffexpr()
endfunc

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

      export def DiffExpr()
        # Prepend some text to check diff type detection
        writefile(['warning', '  message'], v:fname_out)
        silent exe '!diff ' .. v:fname_in .. ' '
                            .. v:fname_new .. '>>' .. v:fname_out
      enddef
  END
  writefile(lines, 'Xdiffexpr', 'D')

  lines =<< trim END
      vim9script
      import './Xdiffexpr' as diff

      set diffexpr=diff.DiffExpr()
      set diffopt=foldcolumn:0
  END
  v9.CheckScriptSuccess(lines)

  enew!
  call setline(1, ['one', 'two', 'three'])
  diffthis

  botright vert new
  call setline(1, ['one', 'two', 'three.'])
  diffthis
  # we only check if this does not cause errors
  redraw

  diffoff!
  set diffexpr=
  set diffopt&
  bwipe!
  bwipe!
enddef

def Test_import_in_patchexpr()
  var lines =<< trim END
    vim9script
    export def TPatch()
      call writefile(['output file'], v:fname_out)
    enddef
  END
  writefile(lines, 'Xpatchexpr', 'D')

  lines =<< trim END
      vim9script
      import './Xpatchexpr' as patch
      set patchexpr=patch.TPatch()
  END
  v9.CheckScriptSuccess(lines)

  call writefile(['input file'], 'Xinput', 'D')
  call writefile(['diff file'], 'Xdiff', 'D')
  :%bwipe!
  edit Xinput
  diffpatch Xdiff
  call assert_equal('output file', getline(1))

  set patchexpr&
  :%bwipe!
enddef

def Test_import_in_formatexpr()
  var lines =<< trim END
      vim9script
      export def MyFormatExpr(): number
        g:did_format = 'yes'
        return 0
      enddef
  END
  writefile(lines, 'Xformatter', 'D')

  lines =<< trim END
      vim9script
      import './Xformatter' as format
      set formatexpr=format.MyFormatExpr()
  END
  v9.CheckScriptSuccess(lines)

  new
  setline(1, ['a', 'b', 'c'])
  normal gqG
  assert_equal('yes', g:did_format)

  bwipe!
  unlet g:did_format
  set formatexpr=
enddef

def Test_import_in_includeexpr()
  writefile(['found it'], 'Xthisfile', 'D')
  new

  var lines =<< trim END
      vim9script
      export def DoSub(): string
        return substitute(v:fname, 'that', 'this', '')
      enddef
  END
  writefile(lines, 'Xinclude.vim', 'D')

  lines =<< trim END
    vim9script
    import './Xinclude.vim'
    set includeexpr=Xinclude.DoSub()
  END
  v9.CheckScriptSuccess(lines)

  setline(1, ['Xthatfile'])
  exe "normal \<C-W>f"
  assert_equal('Xthisfile', expand('%'))

  bwipe!
  bwipe!
  set includeexpr=
enddef

def Test_import_in_indentexpr()
  var lines =<< trim END
      vim9script
      export def GetIndent(): number
        return 5
      enddef
  END
  writefile(lines, 'Xindenter', 'D')

  lines =<< trim END
      vim9script
      import './Xindenter' as indent
      set indentexpr=indent.GetIndent()
      set debug=throw
  END
  v9.CheckScriptSuccess(lines)

  new
  setline(1, 'hello')
  normal ==
  assert_equal('     hello', getline(1))

  bwipe!
  set indentexpr= debug=
enddef

func Test_import_in_printexpr()
  CheckFeature postscript
  call Run_Test_import_in_printexpr()
endfunc

def Run_Test_import_in_printexpr()
  var lines =<< trim END
      vim9script
      export def PrintFile(): bool
        g:printed = 'yes'
        delete('v:fname_in')
        return false
      enddef
  END
  writefile(lines, 'Xprint.vim', 'D')

  lines =<< trim END
      vim9script
      import './Xprint.vim'
      set printexpr=Xprint.PrintFile()
  END
  v9.CheckScriptSuccess(lines)

  help
  hardcopy dummy args
  assert_equal('yes', g:printed)

  set printexpr=
enddef

def Test_import_in_charconvert()
  var lines =<< trim END
      vim9script
      export def MakeUpper(): bool
        var data = readfile(v:fname_in)
        map(data, 'toupper(v:val)')
        writefile(data, v:fname_out)
        return false  # success
      enddef
  END
  writefile(lines, 'Xconvert.vim', 'D')

  lines =<< trim END
      vim9script
      import './Xconvert.vim' as conv
      set charconvert=conv.MakeUpper()
  END
  v9.CheckScriptSuccess(lines)

  writefile(['one', 'two'], 'Xiicfile', 'D')
  new Xiicfile
  write ++enc=ucase Xiicfile1
  assert_equal(['ONE', 'TWO'], readfile('Xiicfile1'))

  delete('Xiicfile1')
  bwipe!
  set charconvert&
enddef

func Test_import_in_spellsuggest_expr()
  CheckFeature spell
  call Run_Test_import_in_spellsuggest_expr()
endfunc

def Run_Test_import_in_spellsuggest_expr()
  var lines =<< trim END
      vim9script
      export def MySuggest(): list<any>
        return [['Fox', 8], ['Fop', 9]]
      enddef
  END
  writefile(lines, 'Xsuggest.vim', 'D')

  lines =<< trim END
      vim9script
      import './Xsuggest.vim' as sugg
      set spell spellsuggest=expr:sugg.MySuggest()
  END
  v9.CheckScriptSuccess(lines)

  set verbose=1  # report errors
  call assert_equal(['Fox', 'Fop'], spellsuggest('Fo', 2))

  set nospell spellsuggest& verbose=0
enddef

def Test_import_in_lambda_method()
  var lines =<< trim END
      vim9script
      export def Retarg(e: any): any
        return e
      enddef
  END
  writefile(lines, 'XexportRetarg.vim', 'D')
  lines =<< trim END
      vim9script
      import './XexportRetarg.vim'
      def Lambda(): string
        var F = (x) => x->XexportRetarg.Retarg()
        return F('arg')
      enddef
      assert_equal('arg', Lambda())
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_export_shadows_global_function()
  mkdir('Xglobdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xglobdir'

  var lines =<< trim END
      vim9script
      export def Shadow(): string
        return 'Shadow()'
      enddef
  END
  writefile(lines, 'Xglobdir/autoload/shadow.vim')

  lines =<< trim END
      vim9script

      def g:Shadow(): string
        return 'global'
      enddef

      import autoload 'shadow.vim'
      assert_equal('Shadow()', shadow.Shadow())
  END
  v9.CheckScriptSuccess(lines)

  delfunc g:Shadow
  bwipe!
  &rtp = save_rtp
enddef

def Test_export_fails()
  v9.CheckScriptFailure(['export var some = 123'], 'E1042:')
  v9.CheckScriptFailure(['vim9script', 'export var g:some'], 'E1022:')
  v9.CheckScriptFailure(['vim9script', 'export echo 134'], 'E1043:')
  v9.CheckScriptFailure(['vim9script', 'export function /a1b2c3'], 'E1044:')

  assert_fails('export echo 1', 'E1043:')
enddef

func Test_import_fails_without_script()
  CheckRunVimInTerminal

  " call indirectly to avoid compilation error for missing functions
  call Run_Test_import_fails_on_command_line()
endfunc

def Run_Test_import_fails_on_command_line()
  var export =<< trim END
    vim9script
    export def Foo(): number
        return 0
    enddef
  END
  writefile(export, 'XexportCmd.vim', 'D')

  var buf = g:RunVimInTerminal('-c "import Foo from ''./XexportCmd.vim''"', {
                rows: 6, wait_for_ruler: 0})
  g:WaitForAssert(() => assert_match('^E1094:', term_getline(buf, 5)))

  g:StopVimInTerminal(buf)
enddef

def Test_vim9_reload_noclear()
  var lines =<< trim END
    vim9script
    export var exported = 'thexport'

    export def TheFunc(x = 0)
    enddef
  END
  writefile(lines, 'XExportReload', 'D')
  lines =<< trim END
    vim9script noclear
    g:loadCount += 1
    var reloaded = 'init'
    import './XExportReload' as exp

    def Again(): string
      return 'again'
    enddef

    exp.TheFunc()

    if exists('loaded') | finish | endif
    var loaded = true

    var notReloaded = 'yes'
    reloaded = 'first'
    def g:Values(): list<string>
      return [reloaded, notReloaded, Again(), Once(), exp.exported]
    enddef

    def Once(): string
      return 'once'
    enddef
  END
  writefile(lines, 'XReloaded', 'D')
  g:loadCount = 0
  source XReloaded
  assert_equal(1, g:loadCount)
  assert_equal(['first', 'yes', 'again', 'once', 'thexport'], g:Values())
  source XReloaded
  assert_equal(2, g:loadCount)
  assert_equal(['init', 'yes', 'again', 'once', 'thexport'], g:Values())
  source XReloaded
  assert_equal(3, g:loadCount)
  assert_equal(['init', 'yes', 'again', 'once', 'thexport'], g:Values())

  delfunc g:Values
  unlet g:loadCount

  lines =<< trim END
      vim9script
      def Inner()
      enddef
  END
  lines->writefile('XreloadScript.vim', 'D')
  source XreloadScript.vim

  lines =<< trim END
      vim9script
      def Outer()
        def Inner()
        enddef
      enddef
      defcompile
  END
  lines->writefile('XreloadScript.vim')
  source XreloadScript.vim
enddef

def Test_vim_reload_noclear_arg_count()
  var lines =<< trim END
      vim9script noclear

      if !exists('g:didload')
        def Test(a: string, b: string)
          echo a b
        enddef
        def Call()
          Test('a', 'b')
        enddef
      else
        # redefine with one argument less
        def Test(a: string)
          echo a
        enddef
      endif
      Call()
      g:didload = 1
  END
  lines->writefile('XreloadScript_1.vim', 'D')
  source XreloadScript_1.vim
  assert_fails('source XreloadScript_1.vim', 'E1106: One argument too many')
  unlet g:didload

  lines =<< trim END
      vim9script noclear

      if !exists('g:didload')
        def Test(a: string, b: string, c: string)
          echo a b
        enddef
        def Call()
          Test('a', 'b', 'c')
        enddef
      else
        # redefine with one argument less
        def Test(a: string)
          echo a
        enddef
      endif
      Call()
      g:didload = 1
  END
  lines->writefile('XreloadScript_2.vim', 'D')
  source XreloadScript_2.vim
  assert_fails('source XreloadScript_2.vim', 'E1106: 2 arguments too many')
  unlet g:didload

  lines =<< trim END
      vim9script noclear

      if !exists('g:didload')
        def Test(a: string)
          echo a
        enddef
        def Call()
          Test('a')
        enddef
      else
        # redefine with one argument extra
        def Test(a: string, b: string)
          echo a b
        enddef
      endif
      Call()
      g:didload = 1
  END
  lines->writefile('XreloadScript_3.vim', 'D')
  source XreloadScript_3.vim
  assert_fails('source XreloadScript_3.vim', 'E1190: One argument too few')
  unlet g:didload

  lines =<< trim END
      vim9script noclear

      if !exists('g:didload')
        def Test(a: string)
          echo a
        enddef
        def Call()
          Test('a')
        enddef
      else
        # redefine with two arguments extra
        def Test(a: string, b: string, c: string)
          echo a b
        enddef
      endif
      Call()
      g:didload = 1
  END
  lines->writefile('XreloadScript_4.vim', 'D')
  source XreloadScript_4.vim
  assert_fails('source XreloadScript_4.vim', 'E1190: 2 arguments too few')
  unlet g:didload
enddef

def Test_vim9_reload_noclear_error()
  var lines =<< trim END
      vim9script noclear

      if !exists('g:didload')
        def Test(a: string)
          echo a
        enddef
        def Call()
          Test('a')
        enddef
      else
        # redefine with a compile error
        def Test(a: string)
          echo ax
        enddef
      endif
      Call()
      g:didload = 1
  END
  lines->writefile('XreloadScriptErr.vim', 'D')
  source XreloadScriptErr.vim
  assert_fails('source XreloadScriptErr.vim', 'E1001: Variable not found: ax')

  unlet g:didload
enddef

def Test_vim9_reload_import()
  var lines =<< trim END
    vim9script
    const var = ''
    var valone = 1234
    def MyFunc(arg: string)
       valone = 5678
    enddef
  END
  var morelines =<< trim END
    var valtwo = 222
    export def GetValtwo(): number
      return valtwo
    enddef
  END
  writefile(lines + morelines, 'Xreload.vim', 'D')
  source Xreload.vim
  source Xreload.vim
  source Xreload.vim

  # cannot declare a var twice
  lines =<< trim END
    vim9script
    var valone = 1234
    var valone = 5678
  END
  writefile(lines, 'Xreload.vim')
  assert_fails('source Xreload.vim', 'E1041:', '', 3, 'Xreload.vim')

  delete('Ximport.vim')
enddef

" if a script is reloaded with a script-local variable that changed its type, a
" compiled function using that variable must fail.
def Test_script_reload_change_type()
  var lines =<< trim END
    vim9script noclear
    var str = 'string'
    def g:GetStr(): string
      return str .. 'xxx'
    enddef
  END
  writefile(lines, 'Xreload.vim', 'D')
  source Xreload.vim
  echo g:GetStr()

  lines =<< trim END
    vim9script noclear
    var str = 1234
  END
  writefile(lines, 'Xreload.vim')
  source Xreload.vim
  assert_fails('echo g:GetStr()', 'E1150:')

  delfunc g:GetStr
enddef

" Define CallFunc so that the test can be compiled
command CallFunc echo 'nop'

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

      if exists('g:loadedThis')
        finish
      endif
      g:loadedThis = 1
      delcommand CallFunc
      command CallFunc Func()
      def Func()
        so XreloadFunc.vim
        g:didTheFunc = 1
      enddef
  END
  writefile(lines, 'XreloadFunc.vim', 'D')
  source XreloadFunc.vim
  CallFunc
  assert_equal(1, g:didTheFunc)

  delcommand CallFunc
  unlet g:loadedThis
  unlet g:didTheFunc
enddef

def s:RetSome(): string
  return 'some'
enddef

" Not exported function that is referenced needs to be accessed by the
" script-local name.
def Test_vim9_funcref()
  var sortlines =<< trim END
      vim9script
      def Compare(i1: number, i2: number): number
        return i2 - i1
      enddef

      export def FastSort(): list<number>
        return range(5)->sort(Compare)
      enddef

      export def GetString(arg: string): string
        return arg
      enddef
  END
  writefile(sortlines, 'Xsort.vim', 'D')

  var lines =<< trim END
    vim9script
    import './Xsort.vim'
    def Test()
      g:result = Xsort.FastSort()
    enddef
    Test()
  END
  writefile(lines, 'Xscript.vim', 'D')
  source Xscript.vim
  assert_equal([4, 3, 2, 1, 0], g:result)
  unlet g:result

  lines =<< trim END
    vim9script
    # using a function imported with "as"
    import './Xsort.vim' as anAlias
    assert_equal('yes', anAlias.GetString('yes'))

    # using the function from a compiled function
    def TestMore(): string
      var s = s:anAlias.GetString('foo')
      return s .. anAlias.GetString('bar')
    enddef
    assert_equal('foobar', TestMore())

    # error when using a function that isn't exported
    assert_fails('anAlias.Compare(1, 2)', 'E1049:')
  END
  writefile(lines, 'Xscript.vim')

  var Funcref = function('s:RetSome')
  assert_equal('some', Funcref())
enddef

" Check that when searching for "FilterFunc" it finds the import in the
" script where FastFilter() is called from, both as a string and as a direct
" function reference.
def Test_vim9_funcref_other_script()
  var filterLines =<< trim END
    vim9script
    export def FilterFunc(idx: number, val: number): bool
      return idx % 2 == 1
    enddef
    export def FastFilter(): list<number>
      return range(10)->filter('FilterFunc(v:key, v:val)')
    enddef
    export def FastFilterDirect(): list<number>
      return range(10)->filter(FilterFunc)
    enddef
  END
  writefile(filterLines, 'Xfilter.vim', 'D')

  var lines =<< trim END
    vim9script
    import './Xfilter.vim' as filter
    def Test()
      var x: list<number> = filter.FastFilter()
    enddef
    Test()
    def TestDirect()
      var x: list<number> = filter.FastFilterDirect()
    enddef
    TestDirect()
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_import_absolute()
  var import_lines = [
        'vim9script',
        'import "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim" as abs',
        'def UseExported()',
        '  g:imported_abs = abs.exported',
        '  abs.exported = 8888',
        '  g:imported_after = abs.exported',
        'enddef',
        'UseExported()',
        'g:import_disassembled = execute("disass UseExported")',
        ]
  writefile(import_lines, 'Ximport_abs.vim', 'D')
  writefile(s:export_script_lines, 'Xexport_abs.vim', 'D')

  source Ximport_abs.vim

  assert_equal(9876, g:imported_abs)
  assert_equal(8888, g:imported_after)
  assert_match('<SNR>\d\+_UseExported\_s*' ..
          'g:imported_abs = abs.exported\_s*' ..
          '0 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' ..
          '1 STOREG g:imported_abs\_s*' ..
          'abs.exported = 8888\_s*' ..
          '2 PUSHNR 8888\_s*' ..
          '3 STORESCRIPT exported-2 in .*Xexport_abs.vim\_s*' ..
          'g:imported_after = abs.exported\_s*' ..
          '4 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' ..
          '5 STOREG g:imported_after',
        g:import_disassembled)

  Undo_export_script_lines()
  unlet g:imported_abs
  unlet g:import_disassembled
enddef

def Test_import_rtp()
  var import_lines = [
        'vim9script',
        'import "Xexport_rtp.vim" as rtp',
        'g:imported_rtp = rtp.exported',
        ]
  writefile(import_lines, 'Ximport_rtp.vim', 'D')
  mkdir('import', 'pR')
  writefile(s:export_script_lines, 'import/Xexport_rtp.vim')

  var save_rtp = &rtp
  &rtp = getcwd()
  source Ximport_rtp.vim
  &rtp = save_rtp

  assert_equal(9876, g:imported_rtp)

  Undo_export_script_lines()
  unlet g:imported_rtp
enddef

def Test_import_compile_error()
  var export_lines = [
        'vim9script',
        'export def ExpFunc(): string',
        '  return notDefined',
        'enddef',
        ]
  writefile(export_lines, 'Xexported.vim', 'D')

  var import_lines = [
        'vim9script',
        'import "./Xexported.vim" as expo',
        'def ImpFunc()',
        '  echo expo.ExpFunc()',
        'enddef',
        'defcompile',
        ]
  writefile(import_lines, 'Ximport.vim', 'D')

  try
    source Ximport.vim
  catch /E1001/
    # Error should be before the Xexported.vim file.
    assert_match('E1001: Variable not found: notDefined', v:exception)
    assert_match('function <SNR>\d\+_ImpFunc\[1\]..<SNR>\d\+_ExpFunc, line 1', v:throwpoint)
  endtry
enddef

def Test_func_overrules_import_fails()
  var export_lines =<< trim END
      vim9script
      export def Func()
        echo 'imported'
      enddef
  END
  writefile(export_lines, 'XexportedFunc.vim', 'D')

  var lines =<< trim END
    vim9script
    import './XexportedFunc.vim' as Func
    def Func()
      echo 'local to function'
    enddef
  END
  v9.CheckScriptFailure(lines, 'E1213: Redefining imported item "Func"')

  lines =<< trim END
    vim9script
    import './XexportedFunc.vim' as Func
    def Outer()
      def Func()
        echo 'local to function'
      enddef
    enddef
    defcompile
  END
  v9.CheckScriptFailure(lines, 'E1236:')
enddef

def Test_source_vim9_from_legacy()
  var vim9_lines =<< trim END
    vim9script
    var local = 'local'
    g:global = 'global'
    export var exported = 'exported'
    export def GetText(): string
       return 'text'
    enddef
  END
  writefile(vim9_lines, 'Xvim9_script.vim', 'D')

  var legacy_lines =<< trim END
    source Xvim9_script.vim

    call assert_false(exists('local'))
    call assert_false(exists('exported'))
    call assert_false(exists('s:exported'))
    call assert_equal('global', global)
    call assert_equal('global', g:global)
  END
  writefile(legacy_lines, 'Xlegacy_script.vim', 'D')

  source Xlegacy_script.vim
  assert_equal('global', g:global)
  unlet g:global

  legacy_lines =<< trim END
    import './Xvim9_script.vim'
    let g:global = s:Xvim9_script.GetText()
  END
  writefile(legacy_lines, 'Xlegacyimport.vim', 'D')
  source Xlegacyimport.vim
  assert_equal('text', g:global)
  unlet g:global
enddef

def Test_import_vim9_from_legacy()
  var vim9_lines =<< trim END
    vim9script
    var local = 'local'
    g:global = 'global'
    export var exported = 'exported'
    export def GetText(): string
       return 'text'
    enddef
    export var exported_nr: number = 22
    def AddNum(n: number)
      exported_nr += n
    enddef
    export var exportedDict: dict<func> = {Fn: AddNum}
    export const CONST = 10
    export final finalVar = 'abc'
  END
  writefile(vim9_lines, 'Xvim9_export.vim', 'D')

  var legacy_lines =<< trim END
    import './Xvim9_export.vim' as vim9

    call assert_false(exists('vim9'))
    call assert_false(exists('local'))
    call assert_false(exists('s:vim9.local'))
    call assert_equal('global', global)
    call assert_equal('global', g:global)
    call assert_false(exists('exported'))
    call assert_false(exists('s:exported'))
    call assert_false(exists('*GetText'))

    " imported symbol is script-local
    call assert_equal('exported', s:vim9.exported)
    call assert_equal('text', s:vim9.GetText())
    call s:vim9.exportedDict.Fn(5)
    call assert_equal(27, s:vim9.exported_nr)
    call call(s:vim9.exportedDict.Fn, [3])
    call assert_equal(30, s:vim9.exported_nr)
    call assert_fails('let s:vim9.CONST = 20', 'E46: Cannot change read-only variable "CONST"')
    call assert_fails('let s:vim9.finalVar = ""', 'E46: Cannot change read-only variable "finalVar"')
    call assert_fails('let s:vim9.non_existing_var = 20', 'E1048: Item not found in script: non_existing_var')
  END
  writefile(legacy_lines, 'Xlegacy_script.vim', 'D')

  source Xlegacy_script.vim
  assert_equal('global', g:global)
  unlet g:global
enddef

def Test_cmdline_win()
  # if the Vim syntax highlighting uses Vim9 constructs they can be used from
  # the command line window.
  mkdir('rtp/syntax', 'pR')
  var export_lines =<< trim END
    vim9script
    export var That = 'yes'
  END
  writefile(export_lines, 'rtp/syntax/Xexport.vim')
  var import_lines =<< trim END
    vim9script
    import './Xexport.vim' as exp
    echo exp.That
  END
  writefile(import_lines, 'rtp/syntax/vim.vim')
  var save_rtp = &rtp
  &rtp = getcwd() .. '/rtp' .. ',' .. &rtp
  syntax on
  augroup CmdWin
    autocmd CmdwinEnter * g:got_there = 'yes'
  augroup END
  # this will open and also close the cmdline window
  feedkeys('q:', 'xt')
  assert_equal('yes', g:got_there)

  augroup CmdWin
    au!
  augroup END
  &rtp = save_rtp
enddef

def Test_import_gone_when_sourced_twice()
  var exportlines =<< trim END
      vim9script
      if exists('g:guard')
        finish
      endif
      g:guard = 1
      export var name = 'someName'
  END
  writefile(exportlines, 'XexportScript.vim', 'D')

  var lines =<< trim END
      vim9script
      import './XexportScript.vim' as expo
      def g:GetName(): string
        return expo.name
      enddef
  END
  writefile(lines, 'XscriptImport.vim', 'D')
  so XscriptImport.vim
  assert_equal('someName', g:GetName())

  so XexportScript.vim
  assert_fails('call g:GetName()', 'E1149:')

  delfunc g:GetName
  unlet g:guard
enddef

" test using an auto-loaded function and variable
def Test_vim9_autoload_full_name()
  var lines =<< trim END
     vim9script
     export def Gettest(): string
       return 'test'
     enddef
     g:some#name = 'name'
     g:some#dict = {key: 'value'}

     export def Varargs(a1: string, ...l: list<string>): string
       return a1 .. l[0] .. l[1]
     enddef
  END

  mkdir('Xfulldir/autoload', 'pR')
  writefile(lines, 'Xfulldir/autoload/some.vim')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xfulldir'

  assert_equal('test', g:some#Gettest())
  assert_equal('name', g:some#name)
  assert_equal('value', g:some#dict.key)
  g:some#other = 'other'
  assert_equal('other', g:some#other)

  assert_equal('abc', some#Varargs('a', 'b', 'c'))

  # upper case script name works
  lines =<< trim END
     vim9script
     export def GetOther(): string
       return 'other'
     enddef
  END
  writefile(lines, 'Xfulldir/autoload/Other.vim')
  assert_equal('other', g:Other#GetOther())

  &rtp = save_rtp
enddef

def Test_vim9script_autoload()
  mkdir('Xaldir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xaldir'

  # when the path has "/autoload/" prefix is not needed
  var lines =<< trim END
     vim9script
     g:prefixed_loaded += 1

     export def Gettest(): string
       return 'test'
     enddef

     export var name = 'name'

     export func GetFunc()
       return Gettest() .. 'more' .. s:name
     endfunc

     export def GetDef(): string
       return Gettest() .. 'more' .. name
     enddef

     export final fname = 'final'
     export const cname = 'const'
  END
  writefile(lines, 'Xaldir/autoload/prefixed.vim')

  g:prefixed_loaded = 0
  g:expected_loaded = 0
  lines =<< trim END
      vim9script
      import autoload 'prefixed.vim'
      assert_equal(g:expected_loaded, g:prefixed_loaded)
      assert_equal('test', prefixed.Gettest())
      assert_equal(1, g:prefixed_loaded)

      assert_equal('testmorename', prefixed.GetFunc())
      assert_equal('testmorename', prefixed.GetDef())
      assert_equal('name', prefixed.name)
      assert_equal('final', prefixed.fname)
      assert_equal('const', prefixed.cname)
  END
  v9.CheckScriptSuccess(lines)
  # can source it again, autoload script not loaded again
  g:expected_loaded = 1
  v9.CheckScriptSuccess(lines)

  # can also get the items by autoload name
  lines =<< trim END
      call assert_equal('test', prefixed#Gettest())
      call assert_equal('testmorename', prefixed#GetFunc())
      call assert_equal('name', prefixed#name)
      call assert_equal('final', prefixed#fname)
      call assert_equal('const', prefixed#cname)
  END
  v9.CheckScriptSuccess(lines)

  unlet g:prefixed_loaded
  unlet g:expected_loaded
  &rtp = save_rtp
enddef

def Test_import_autoload_not_exported()
  mkdir('Xnimdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xnimdir'

  # error when using an item that is not exported from an autoload script
  var exportLines =<< trim END
      vim9script
      var notExported = 123
      def NotExport()
        echo 'nop'
      enddef
  END
  writefile(exportLines, 'Xnimdir/autoload/notExport1.vim')

  var lines =<< trim END
      vim9script
      import autoload 'notExport1.vim'
      echo notExport1.notFound
  END
  v9.CheckScriptFailure(lines, 'E1048: Item not found in script: notFound')

  lines =<< trim END
      vim9script
      import autoload 'notExport1.vim'
      echo notExport1.notExported
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notExported')

  lines =<< trim END
      vim9script
      import autoload 'notExport1.vim'
      echo notExport1.NotFunc()
  END
  v9.CheckScriptFailure(lines, 'E1048: Item not found in script: NotFunc')

  lines =<< trim END
      vim9script
      import autoload 'notExport1.vim'
      echo notExport1.NotExport()
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExport')

  lines =<< trim END
      vim9script
      import autoload 'notExport1.vim'
      echo 'text'->notExport1.NotFunc()
  END
  v9.CheckScriptFailure(lines, 'E1048: Item not found in script: NotFunc')

  lines =<< trim END
      vim9script
      import autoload 'notExport1.vim'
      echo 'text'->notExport1.NotExport()
  END
  v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExport')

  # using a :def function we use a different autoload script every time so that
  # the function is compiled without the script loaded
  writefile(exportLines, 'Xnimdir/autoload/notExport2.vim')
  lines =<< trim END
      vim9script
      import autoload 'notExport2.vim'
      def Testit()
        echo notExport2.notFound
      enddef
      Testit()
  END
  v9.CheckScriptFailure(lines, 'E1048: Item not found in script: notExport2#notFound')

  writefile(exportLines, 'Xnimdir/autoload/notExport3.vim')
  lines =<< trim END
      vim9script
      import autoload 'notExport3.vim'
      def Testit()
        echo notExport3.notExported
      enddef
      Testit()
  END
  # don't get E1049 because it is too complicated to figure out
  v9.CheckScriptFailure(lines, 'E1048: Item not found in script: notExport3#notExported')

  writefile(exportLines, 'Xnimdir/autoload/notExport4.vim')
  lines =<< trim END
      vim9script
      import autoload 'notExport4.vim'
      def Testit()
        echo notExport4.NotFunc()
      enddef
      Testit()
  END
  v9.CheckScriptFailure(lines, 'E117: Unknown function: notExport4#NotFunc')

  writefile(exportLines, 'Xnimdir/autoload/notExport5.vim')
  lines =<< trim END
      vim9script
      import autoload 'notExport5.vim'
      def Testit()
        echo notExport5.NotExport()
      enddef
      Testit()
  END
  v9.CheckScriptFailure(lines, 'E117: Unknown function: notExport5#NotExport')

  writefile(exportLines, 'Xnimdir/autoload/notExport6.vim')
  lines =<< trim END
      vim9script
      import autoload 'notExport6.vim'
      def Testit()
        echo 'text'->notExport6.NotFunc()
      enddef
      Testit()
  END
  v9.CheckScriptFailure(lines, 'E117: Unknown function: notExport6#NotFunc')

  writefile(exportLines, 'Xnimdir/autoload/notExport7.vim')
  lines =<< trim END
      vim9script
      import autoload 'notExport7.vim'
      def Testit()
        echo 'text'->notExport7.NotExport()
      enddef
      Testit()
  END
  v9.CheckScriptFailure(lines, 'E117: Unknown function: notExport7#NotExport')

  &rtp = save_rtp
enddef

def Test_vim9script_autoload_call()
  mkdir('Xcalldir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xcalldir'

  var lines =<< trim END
     vim9script

     export def RetArg(arg: string): string
       return arg
     enddef

     export def Getother()
       g:result = 'other'
     enddef
  END
  writefile(lines, 'Xcalldir/autoload/another.vim')

  lines =<< trim END
      vim9script
      import autoload 'another.vim'

      # compile this before 'another.vim' is loaded
      def CallAnother()
        assert_equal('foo', 'foo'->another.RetArg())
      enddef
      CallAnother()

      call another.Getother()
      assert_equal('other', g:result)

      assert_equal('arg', call('another.RetArg', ['arg']))

      verbose function another.Getother
      # should we disallow this?
      verbose function another#Getother
  END
  v9.CheckScriptSuccess(lines)

  unlet g:result
  &rtp = save_rtp
enddef

def Test_vim9script_noclear_autoload()
  mkdir('Xnocdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xnocdir'

  var lines =<< trim END
      vim9script
      export def Func(): string
        return 'called'
      enddef
      g:double_loaded = 'yes'
  END
  writefile(lines, 'Xnocdir/autoload/double.vim')

  lines =<< trim END
      vim9script noclear
      if exists('g:script_loaded')
        finish
      endif
      g:script_loaded = true

      import autoload 'double.vim'
      nnoremap <F3> <ScriptCmd>g:result = double.Func()<CR>
  END
  g:double_loaded = 'no'
  writefile(lines, 'Xloaddouble', 'D')
  source Xloaddouble
  assert_equal('no', g:double_loaded)
  assert_equal(true, g:script_loaded)
  source Xloaddouble
  feedkeys("\<F3>", 'xt')
  assert_equal('called', g:result)
  assert_equal('yes', g:double_loaded)

  unlet g:double_loaded
  unlet g:script_loaded
  unlet g:result
  &rtp = save_rtp
enddef

def Test_vim9script_autoload_duplicate()
  mkdir('Xdupdir/autoload', 'pR')

  var lines =<< trim END
     vim9script

     export def Func()
     enddef

     def Func()
     enddef
  END
  writefile(lines, 'Xdupdir/autoload/dupfunc.vim')
  assert_fails('source Xdupdir/autoload/dupfunc.vim', 'E1073:')

  lines =<< trim END
     vim9script

     def Func()
     enddef

     export def Func()
     enddef
  END
  writefile(lines, 'Xdupdir/autoload/dup2func.vim')
  assert_fails('source Xdupdir/autoload/dup2func.vim', 'E1073:')

  lines =<< trim END
     vim9script

     def Func()
     enddef

     export var Func = 'asdf'
  END
  writefile(lines, 'Xdupdir/autoload/dup3func.vim')
  assert_fails('source Xdupdir/autoload/dup3func.vim', 'E1041: Redefining script item: "Func"')

  lines =<< trim END
     vim9script

     export var Func = 'asdf'

     def Func()
     enddef
  END
  writefile(lines, 'Xdupdir/autoload/dup4func.vim')
  assert_fails('source Xdupdir/autoload/dup4func.vim', 'E1041:')

  lines =<< trim END
     vim9script

     var Func = 'asdf'

     export def Func()
     enddef
  END
  writefile(lines, 'Xdupdir/autoload/dup5func.vim')
  assert_fails('source Xdupdir/autoload/dup5func.vim', 'E707:')

  lines =<< trim END
     vim9script

     export def Func()
     enddef

     var Func = 'asdf'
  END
  writefile(lines, 'Xdupdir/autoload/dup6func.vim')
  assert_fails('source Xdupdir/autoload/dup6func.vim', 'E1041: Redefining script item: "Func"')
enddef

def Test_autoload_missing_function_name()
  mkdir('Xmisdir/autoload', 'pR')

  var lines =<< trim END
     vim9script

     def loadme#()
     enddef
  END
  writefile(lines, 'Xmisdir/autoload/loadme.vim')
  assert_fails('source Xmisdir/autoload/loadme.vim', 'E129:')
enddef

def Test_autoload_name_wrong()
  var lines =<< trim END
     def Xscriptname#Func()
     enddef
  END
  writefile(lines, 'Xscriptname.vim', 'D')
  v9.CheckScriptFailure(lines, 'E746:')

  mkdir('Xwrodir/autoload', 'pR')
  lines =<< trim END
     vim9script
     def somescript#Func()
     enddef
  END
  writefile(lines, 'Xwrodir/autoload/somescript.vim')
  assert_fails('source Xwrodir/autoload/somescript.vim', 'E1263:')

  delete('Xwrodir', 'rf')
enddef

def Test_import_autoload_postponed()
  mkdir('Xpostdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xpostdir'

  var lines =<< trim END
      vim9script

      g:loaded_postponed = 'true'
      export var variable = 'bla'
      export def Function(): string
        return 'bla'
      enddef
  END
  writefile(lines, 'Xpostdir/autoload/postponed.vim')

  lines =<< trim END
      vim9script

      import autoload 'postponed.vim'
      def Tryit()
        echo postponed.variable
        echo postponed.Function()
      enddef
      defcompile
  END
  v9.CheckScriptSuccess(lines)
  assert_false(exists('g:loaded_postponed'))
  v9.CheckScriptSuccess(lines + ['Tryit()'])
  assert_equal('true', g:loaded_postponed)

  unlet g:loaded_postponed
  &rtp = save_rtp
enddef

def Test_import_autoload_override()
  mkdir('Xoverdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xoverdir'
  test_override('autoload', 1)

  var lines =<< trim END
      vim9script

      g:loaded_override = 'true'
      export var variable = 'bla'
      export def Function(): string
        return 'bla'
      enddef
  END
  writefile(lines, 'Xoverdir/autoload/override.vim')

  lines =<< trim END
      vim9script

      import autoload 'override.vim'
      assert_equal('true', g:loaded_override)

      def Tryit()
        echo override.doesNotExist
      enddef
      defcompile
  END
  v9.CheckScriptFailure(lines, 'E1048: Item not found in script: doesNotExist', 1)

  test_override('autoload', 0)
  unlet g:loaded_override
  &rtp = save_rtp
enddef

def Test_autoload_mapping()
  mkdir('Xmapdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xmapdir'

  var lines =<< trim END
      vim9script

      g:toggle_loaded = 'yes'

      export def Toggle(): string
        return ":g:toggle_called = 'yes'\<CR>"
      enddef
      export def Doit()
        g:doit_called = 'yes'
      enddef
  END
  writefile(lines, 'Xmapdir/autoload/toggle.vim')

  lines =<< trim END
      vim9script

      import autoload 'toggle.vim'

      nnoremap <silent> <expr> tt toggle.Toggle()
      nnoremap <silent> xx <ScriptCmd>toggle.Doit()<CR>
      nnoremap <silent> yy <Cmd>toggle.Doit()<CR>
  END
  v9.CheckScriptSuccess(lines)
  assert_false(exists("g:toggle_loaded"))
  assert_false(exists("g:toggle_called"))
  assert_match('\d A: \f*[/\\]toggle.vim', execute('scriptnames'))

  feedkeys("tt", 'xt')
  assert_equal('yes', g:toggle_loaded)
  assert_equal('yes', g:toggle_called)
  assert_match('\d: \f*[/\\]toggle.vim', execute('scriptnames'))

  feedkeys("xx", 'xt')
  assert_equal('yes', g:doit_called)

  assert_fails('call feedkeys("yy", "xt")', 'E121: Undefined variable: toggle')

  nunmap tt
  nunmap xx
  nunmap yy
  unlet g:toggle_loaded
  unlet g:toggle_called
  &rtp = save_rtp
enddef

def Test_vim9script_autoload_fails()
  var lines =<< trim END
      vim9script autoload
      var n = 0
  END
  v9.CheckScriptFailure(lines, 'E475: Invalid argument: autoload')

  lines =<< trim END
      vim9script noclear noclear
      var n = 0
  END
  v9.CheckScriptFailure(lines, 'E983: Duplicate argument: noclear')

  lines =<< trim END
      vim9script noclears
      var n = 0
  END
  v9.CheckScriptFailure(lines, 'E475: Invalid argument: noclears')
enddef

def Test_import_autoload_fails()
  var lines =<< trim END
      vim9script
      import autoload autoload 'prefixed.vim'
  END
  v9.CheckScriptFailure(lines, 'E121: Undefined variable: autoload')

  lines =<< trim END
      vim9script
      import autoload './doesNotExist.vim'
  END
  v9.CheckScriptFailure(lines, 'E282:', 2)

  lines =<< trim END
      vim9script
      import autoload '/dir/doesNotExist.vim'
  END
  v9.CheckScriptFailure(lines, 'E282:', 2)

  lines =<< trim END
      vim9script
      import autoload '../testdir'
  END
  v9.CheckScriptFailure(lines, 'E17:', 2)

  lines =<< trim END
      vim9script
      import autoload 'doesNotExist.vim'
  END
  v9.CheckScriptFailure(lines, 'E1053: Could not import "doesNotExist.vim"')
enddef

" test disassembling an auto-loaded function starting with "debug"
def Test_vim9_autoload_disass()
  mkdir('Xdasdir/autoload', 'pR')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xdasdir'

  var lines =<< trim END
     vim9script
     export def Test(): string
       return 'debug'
     enddef
  END
  writefile(lines, 'Xdasdir/autoload/debugit.vim')

  lines =<< trim END
     vim9script
     export def Test(): string
       return 'profile'
     enddef
  END
  writefile(lines, 'Xdasdir/autoload/profileit.vim')

  lines =<< trim END
    vim9script
    assert_equal('debug', debugit#Test())
    disass debugit#Test
    assert_equal('profile', profileit#Test())
    disass profileit#Test
  END
  v9.CheckScriptSuccess(lines)

  &rtp = save_rtp
enddef

" test using a vim9script that is auto-loaded from an autocmd
def Test_vim9_aucmd_autoload()
  var lines =<< trim END
     vim9script
     export def Test()
         echomsg getreg('"')
     enddef
  END

  mkdir('Xauldir/autoload', 'pR')
  writefile(lines, 'Xauldir/autoload/foo.vim')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xauldir'
  augroup test
    autocmd TextYankPost * call foo#Test()
  augroup END

  normal Y

  augroup test
    autocmd!
  augroup END
  &rtp = save_rtp
enddef

" test using a autoloaded file that is case sensitive
def Test_vim9_autoload_case_sensitive()
  var lines =<< trim END
     vim9script
     export def CaseSensitive(): string
       return 'done'
     enddef
  END

  mkdir('Xcasedir/autoload', 'pR')
  writefile(lines, 'Xcasedir/autoload/CaseSensitive.vim')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xcasedir'

  lines =<< trim END
      vim9script
      import autoload 'CaseSensitive.vim'
      assert_equal('done', CaseSensitive.CaseSensitive())
  END
  v9.CheckScriptSuccess(lines)

  if !has('fname_case')
    lines =<< trim END
        vim9script
        import autoload 'CaseSensitive.vim'
        import autoload 'casesensitive.vim'
    END
    v9.CheckScriptFailure(lines, 'E1262:')
  endif

  &rtp = save_rtp
enddef

" This was causing a crash because suppress_errthrow wasn't reset.
def Test_vim9_autoload_error()
  var lines =<< trim END
      vim9script
      def crash#func()
          try
              for x in List()
              endfor
          catch
          endtry
          g:ok = true
      enddef
      fu List()
          invalid
      endfu
      try
          alsoinvalid
      catch /wontmatch/
      endtry
  END
  call mkdir('Xruntime/autoload', 'pR')
  call writefile(lines, 'Xruntime/autoload/crash.vim')

  # run in a separate Vim to avoid the side effects of assert_fails()
  lines =<< trim END
    exe 'set rtp^=' .. getcwd() .. '/Xruntime'
    call crash#func()
    call writefile(['ok'], 'Xdidit')
    qall!
  END
  writefile(lines, 'Xscript', 'D')
  g:RunVim([], [], '-S Xscript')
  assert_equal(['ok'], readfile('Xdidit'))

  delete('Xdidit')

  lines =<< trim END
    vim9script
    var foo#bar = 'asdf'
  END
  v9.CheckScriptFailure(lines, 'E461: Illegal variable name: foo#bar', 2)
enddef

def Test_vim9_import_symlink()
  if !has('unix')
    CheckUnix
  else
    mkdir('Xto/plugin', 'pR')
    var lines =<< trim END
        vim9script
        import autoload 'bar.vim'
        def FooFunc(): string
          return bar.Func()
        enddef
        g:resultFunc = FooFunc()
        g:resultValue = bar.value
    END
    writefile(lines, 'Xto/plugin/foo.vim')

    mkdir('Xto/autoload', 'pR')
    lines =<< trim END
        vim9script
        export def Func(): string
          return 'func'
        enddef
        export var value = 'val'
    END
    writefile(lines, 'Xto/autoload/bar.vim')

    var save_rtp = &rtp
    &rtp = getcwd() .. '/Xfrom'
    system('ln -s ' .. getcwd() .. '/Xto Xfrom')

    source Xfrom/plugin/foo.vim
    assert_equal('func', g:resultFunc)
    assert_equal('val', g:resultValue)

    var infoTo = getscriptinfo()->filter((_, v) => v.name =~ 'Xto/autoload/bar')
    var infoFrom = getscriptinfo()->filter((_, v) => v.name =~ 'Xfrom/autoload/bar')
    assert_equal(1, len(infoTo))
    assert_equal(1, len(infoFrom))
    assert_equal(infoTo[0].sid, infoFrom[0].sourced)
    var output: string
    redir => output
    scriptnames
    redir END
    assert_match(infoFrom[0].sid .. '->' .. infoFrom[0].sourced .. '.*Xfrom', output)

    unlet g:resultFunc
    unlet g:resultValue
    &rtp = save_rtp
    delete('Xfrom', 'rf')

    # Access item from :def imported through symbolic linked directory. #14536
    mkdir('Xto/real_dir', 'pR')
    lines =<< trim END
        vim9script
        export const val = 17
        export def F(): number
          return 23
        enddef
    END
    writefile(lines, 'Xto/real_dir/real_file.vim')
    system('ln -s real_dir Xto/syml_dir')
    defer delete('Xto/syml_dir')
    lines =<< trim END
      vim9script
      import autoload './Xto/syml_dir/real_file.vim'

      def Fmain()
        assert_equal(17, real_file.val)
      enddef
      def F2()
        assert_equal(23, real_file.F())
      enddef
      Fmain()
      F2()
    END
    v9.CheckScriptSuccess(lines)
  endif
enddef

def Test_export_in_conditional_block()
  var lines =<< trim END
      vim9script
      if exists('this_will_fail')
        export var MyVar = "hello"
      endif
  END
  v9.CheckScriptSuccess(lines)
enddef

" Import fails when an autoloaded script is imported again.
" Github issue #14171
def Test_import_autloaded_script()
  mkdir('Ximporttwice', 'pR')
  mkdir('Ximporttwice/plugin')
  mkdir('Ximporttwice/autoload')
  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Ximporttwice'

  var lines =<< trim END
    vim9script

    export def H(): number
      return 10
    enddef
  END
  writefile(lines, 'Ximporttwice/autoload/hello.vim')

  lines =<< trim END
    vim9script

    import "./hello.vim"
    export def W(): number
      return 20
    enddef
  END
  writefile(lines, 'Ximporttwice/autoload/world.vim')

  lines =<< trim END
    vim9script

    import autoload '../autoload/hello.vim'
    import autoload '../autoload/world.vim'

    command Hello echo hello.H()
    command World echo world.W()
  END
  writefile(lines, 'Ximporttwice/plugin/main.vim')

  lines =<< trim END
    vim9script

    source ./Ximporttwice/plugin/main.vim
    assert_equal(['20'], execute('World')->split("\n"))
  END
  v9.CheckScriptSuccess(lines)

  &rtp = save_rtp
enddef

" Test for autoloading an imported dict func
def Test_autoload_import_dict_func()
  mkdir('Xdir/autoload', 'pR')
  var lines =<< trim END
    vim9script
    export var al_exported_nr: number = 33
    def Al_AddNum(n: number)
      al_exported_nr += n
    enddef
    export var al_exportedDict: dict<func> = {Fn: Al_AddNum}
  END
  writefile(lines, 'Xdir/autoload/Xdictfunc.vim')

  var save_rtp = &rtp
  exe 'set rtp^=' .. getcwd() .. '/Xdir'
  lines =<< trim END
    import './Xdir/autoload/Xdictfunc.vim'
    call Xdictfunc#al_exportedDict.Fn(5)
    call assert_equal(38, Xdictfunc#al_exported_nr)
    call call(Xdictfunc#al_exportedDict.Fn, [3])
    call assert_equal(41, Xdictfunc#al_exported_nr)
  END
  v9.CheckScriptSuccess(lines)
  &rtp = save_rtp
enddef

" Test for changing the value of an imported Dict item
def Test_set_imported_dict_item()
  var lines =<< trim END
    vim9script
    export var dict1: dict<bool> = {bflag: false}
    export var dict2: dict<dict<bool>> = {x: {bflag: false}}
  END
  writefile(lines, 'XimportedDict.vim', 'D')

  lines =<< trim END
    vim9script
    import './XimportedDict.vim'
    assert_equal(XimportedDict.dict1.bflag, false)
    XimportedDict.dict1.bflag = true
    assert_equal(XimportedDict.dict1.bflag, true)
    XimportedDict.dict2.x.bflag = true
    assert_equal(XimportedDict.dict2.x.bflag, true)
    assert_equal('bool', typename(XimportedDict.dict1.bflag))
    assert_equal('bool', typename(XimportedDict.dict2.x.bflag))
    assert_equal('bool', typename(XimportedDict.dict2['x'].bflag))
    assert_equal('bool', typename(XimportedDict.dict2.x['bflag']))

    assert_equal(XimportedDict.dict1['bflag'], true)
    XimportedDict.dict1['bflag'] = false
    assert_equal(XimportedDict.dict1.bflag, false)
    XimportedDict.dict2['x']['bflag'] = false
    assert_equal(XimportedDict.dict2['x'].bflag, false)
  END
  v9.CheckScriptSuccess(lines)

  lines =<< trim END
    vim9script
    import './XimportedDict.vim'
    XimportedDict.dict2.x.bflag = []
  END
  v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected bool but got list<any>', 3)
enddef

" Test for changing the value of an imported class member
def Test_set_imported_class_member()
  var lines =<< trim END
    vim9script
    export class Config
      public static var option = false
    endclass
  END
  writefile(lines, 'XimportedClass.vim', 'D')

  lines =<< trim END
    vim9script
    import './XimportedClass.vim' as foo
    type FooConfig = foo.Config
    assert_equal(false, FooConfig.option)
    assert_equal(false, foo.Config.option)
    foo.Config.option = true
    assert_equal(true, foo.Config.option)
    assert_equal(true, FooConfig.option)
  END
  v9.CheckScriptSuccess(lines)
enddef

" Test for using an imported function from the vimrc file.  The function is
" defined in the 'start' directory of a package.
def Test_import_from_vimrc()
  mkdir('Ximport/pack/foobar/start/foo/autoload', 'pR')
  var lines =<< trim END
    vim9script
    export def Foo()
      writefile(['Foo called'], 'Xoutput.log')
    enddef
  END
  writefile(lines, 'Ximport/pack/foobar/start/foo/autoload/foo.vim')
  lines =<< trim END
    vim9script
    set packpath+=./Ximport
    try
      import autoload 'foo.vim'
      foo.Foo()
    catch
      writefile(['Failed to import foo.vim'], 'Xoutput.log')
    endtry
    qall!
  END
  writefile(lines, 'Xvimrc', 'D')
  g:RunVim([], [], '-u Xvimrc')
  assert_equal(['Foo called'], readfile('Xoutput.log'))
  delete('Xoutput.log')
enddef

" Test for changing a locked imported variable
def Test_import_locked_var()
  var lines =<< trim END
    vim9script
    export var Foo: number = 10
    lockvar Foo
  END
  writefile(lines, 'Ximportlockedvar.vim', 'D')
  lines =<< trim END
    vim9script
    import './Ximportlockedvar.vim' as Bar
    Bar.Foo = 20
  END
  v9.CheckScriptFailure(lines, 'E741: Value is locked: Foo', 3)
enddef

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