| " Test Vim9 classes |
| |
| source check.vim |
| import './vim9.vim' as v9 |
| |
| def Test_class_basic() |
| var lines =<< trim END |
| class NotWorking |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1316:') |
| |
| lines =<< trim END |
| vim9script |
| class notWorking |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1314:') |
| |
| lines =<< trim END |
| vim9script |
| class Not@working |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1315:') |
| |
| lines =<< trim END |
| vim9script |
| abstract noclass Something |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E475:') |
| |
| lines =<< trim END |
| vim9script |
| abstract classy Something |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E475:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| endcl |
| END |
| v9.CheckScriptFailure(lines, 'E1065:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| endclass school's out |
| END |
| v9.CheckScriptFailure(lines, 'E488:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| endclass | echo 'done' |
| END |
| v9.CheckScriptFailure(lines, 'E488:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| this |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1317:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| this. |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1317:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| this .count |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1317:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| this. count |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1317:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| this.count: number |
| that.count |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1318: Not a valid command in a class: that.count') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| this.count |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1022:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| def new() |
| this.state = 0 |
| enddef |
| endclass |
| var obj = Something.new() |
| END |
| v9.CheckScriptFailure(lines, 'E1089:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| this.count : number |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1059:') |
| |
| lines =<< trim END |
| vim9script |
| class Something |
| this.count:number |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1069:') |
| |
| # Test for unsupported comment specifier |
| lines =<< trim END |
| vim9script |
| class Something |
| # comment |
| #{ |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1170:') |
| |
| lines =<< trim END |
| vim9script |
| |
| class TextPosition |
| this.lnum: number |
| this.col: number |
| |
| # make a nicely formatted string |
| def ToString(): string |
| return $'({this.lnum}, {this.col})' |
| enddef |
| endclass |
| |
| # use the automatically generated new() method |
| var pos = TextPosition.new(2, 12) |
| assert_equal(2, pos.lnum) |
| assert_equal(12, pos.col) |
| |
| # call an object method |
| assert_equal('(2, 12)', pos.ToString()) |
| |
| assert_equal(v:t_class, type(TextPosition)) |
| assert_equal(v:t_object, type(pos)) |
| assert_equal('class<TextPosition>', typename(TextPosition)) |
| assert_equal('object<TextPosition>', typename(pos)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # When referencing object methods, space cannot be used after a "." |
| lines =<< trim END |
| vim9script |
| class A |
| def Foo(): number |
| return 10 |
| enddef |
| endclass |
| var a = A.new() |
| var v = a. Foo() |
| END |
| v9.CheckScriptFailure(lines, 'E1202:') |
| |
| # Using an object without specifying a method or a member variable |
| lines =<< trim END |
| vim9script |
| class A |
| def Foo(): number |
| return 10 |
| enddef |
| endclass |
| var a = A.new() |
| var v = a. |
| END |
| v9.CheckScriptFailure(lines, 'E15:') |
| |
| # Error when parsing the arguments of an object method. |
| lines =<< trim END |
| vim9script |
| class A |
| def Foo() |
| enddef |
| endclass |
| var a = A.new() |
| var v = a.Foo(,) |
| END |
| v9.CheckScriptFailure(lines, 'E15:') |
| enddef |
| |
| def Test_class_defined_twice() |
| # class defined twice should fail |
| var lines =<< trim END |
| vim9script |
| class There |
| endclass |
| class There |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1041: Redefining script item: "There"') |
| |
| # one class, reload same script twice is OK |
| lines =<< trim END |
| vim9script |
| class There |
| endclass |
| END |
| writefile(lines, 'XclassTwice.vim', 'D') |
| source XclassTwice.vim |
| source XclassTwice.vim |
| enddef |
| |
| def Test_returning_null_object() |
| # this was causing an internal error |
| var lines =<< trim END |
| vim9script |
| |
| class BufferList |
| def Current(): any |
| return null_object |
| enddef |
| endclass |
| |
| var buffers = BufferList.new() |
| echo buffers.Current() |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_using_null_class() |
| var lines =<< trim END |
| @_ = null_class.member |
| END |
| v9.CheckDefExecAndScriptFailure(lines, ['E715:', 'E1363:']) |
| enddef |
| |
| def Test_class_interface_wrong_end() |
| var lines =<< trim END |
| vim9script |
| abstract class SomeName |
| this.member = 'text' |
| endinterface |
| END |
| v9.CheckScriptFailure(lines, 'E476: Invalid command: endinterface, expected endclass') |
| |
| lines =<< trim END |
| vim9script |
| export interface AnotherName |
| this.member: string |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E476: Invalid command: endclass, expected endinterface') |
| enddef |
| |
| def Test_object_not_set() |
| var lines =<< trim END |
| vim9script |
| |
| class State |
| this.value = 'xyz' |
| endclass |
| |
| var state: State |
| var db = {'xyz': 789} |
| echo db[state.value] |
| END |
| v9.CheckScriptFailure(lines, 'E1360:') |
| |
| lines =<< trim END |
| vim9script |
| |
| class Class |
| this.id: string |
| def Method1() |
| echo 'Method1' .. this.id |
| enddef |
| endclass |
| |
| var obj: Class |
| def Func() |
| obj.Method1() |
| enddef |
| Func() |
| END |
| v9.CheckScriptFailure(lines, 'E1360:') |
| |
| lines =<< trim END |
| vim9script |
| |
| class Background |
| this.background = 'dark' |
| endclass |
| |
| class Colorscheme |
| this._bg: Background |
| |
| def GetBackground(): string |
| return this._bg.background |
| enddef |
| endclass |
| |
| var bg: Background # UNINITIALIZED |
| echo Colorscheme.new(bg).GetBackground() |
| END |
| v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object<Background> but got object<Unknown>') |
| |
| # TODO: this should not give an error but be handled at runtime |
| lines =<< trim END |
| vim9script |
| |
| class Class |
| this.id: string |
| def Method1() |
| echo 'Method1' .. this.id |
| enddef |
| endclass |
| |
| var obj = null_object |
| def Func() |
| obj.Method1() |
| enddef |
| Func() |
| END |
| v9.CheckScriptFailure(lines, 'E1363:') |
| enddef |
| |
| def Test_class_member_initializer() |
| var lines =<< trim END |
| vim9script |
| |
| class TextPosition |
| this.lnum: number = 1 |
| this.col: number = 1 |
| |
| # constructor with only the line number |
| def new(lnum: number) |
| this.lnum = lnum |
| enddef |
| endclass |
| |
| var pos = TextPosition.new(3) |
| assert_equal(3, pos.lnum) |
| assert_equal(1, pos.col) |
| |
| var instr = execute('disassemble TextPosition.new') |
| assert_match('new\_s*' .. |
| '0 NEW TextPosition size \d\+\_s*' .. |
| '\d PUSHNR 1\_s*' .. |
| '\d STORE_THIS 0\_s*' .. |
| '\d PUSHNR 1\_s*' .. |
| '\d STORE_THIS 1\_s*' .. |
| 'this.lnum = lnum\_s*' .. |
| '\d LOAD arg\[-1]\_s*' .. |
| '\d PUSHNR 0\_s*' .. |
| '\d LOAD $0\_s*' .. |
| '\d\+ STOREINDEX object\_s*' .. |
| '\d\+ RETURN object.*', |
| instr) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_member_any_used_as_object() |
| var lines =<< trim END |
| vim9script |
| |
| class Inner |
| this.value: number = 0 |
| endclass |
| |
| class Outer |
| this.inner: any |
| endclass |
| |
| def F(outer: Outer) |
| outer.inner.value = 1 |
| enddef |
| |
| var inner_obj = Inner.new(0) |
| var outer_obj = Outer.new(inner_obj) |
| F(outer_obj) |
| assert_equal(1, inner_obj.value) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class Inner |
| this.value: number = 0 |
| endclass |
| |
| class Outer |
| this.inner: Inner |
| endclass |
| |
| def F(outer: Outer) |
| outer.inner.value = 1 |
| enddef |
| |
| def Test_assign_to_nested_typed_member() |
| var inner = Inner.new(0) |
| var outer = Outer.new(inner) |
| F(outer) |
| assert_equal(1, inner.value) |
| enddef |
| |
| Test_assign_to_nested_typed_member() |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_assignment_with_operator() |
| var lines =<< trim END |
| vim9script |
| |
| class Foo |
| this.x: number |
| |
| def Add(n: number) |
| this.x += n |
| enddef |
| endclass |
| |
| var f = Foo.new(3) |
| f.Add(17) |
| assert_equal(20, f.x) |
| |
| def AddToFoo(obj: Foo) |
| obj.x += 3 |
| enddef |
| |
| AddToFoo(f) |
| assert_equal(23, f.x) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_list_of_objects() |
| var lines =<< trim END |
| vim9script |
| |
| class Foo |
| def Add() |
| enddef |
| endclass |
| |
| def ProcessList(fooList: list<Foo>) |
| for foo in fooList |
| foo.Add() |
| endfor |
| enddef |
| |
| var l: list<Foo> = [Foo.new()] |
| ProcessList(l) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_expr_after_using_object() |
| var lines =<< trim END |
| vim9script |
| |
| class Something |
| this.label: string = '' |
| endclass |
| |
| def Foo(): Something |
| var v = Something.new() |
| echo 'in Foo(): ' .. typename(v) |
| return v |
| enddef |
| |
| Foo() |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_class_default_new() |
| var lines =<< trim END |
| vim9script |
| |
| class TextPosition |
| this.lnum: number = 1 |
| this.col: number = 1 |
| endclass |
| |
| var pos = TextPosition.new() |
| assert_equal(1, pos.lnum) |
| assert_equal(1, pos.col) |
| |
| pos = TextPosition.new(v:none, v:none) |
| assert_equal(1, pos.lnum) |
| assert_equal(1, pos.col) |
| |
| pos = TextPosition.new(3, 22) |
| assert_equal(3, pos.lnum) |
| assert_equal(22, pos.col) |
| |
| pos = TextPosition.new(v:none, 33) |
| assert_equal(1, pos.lnum) |
| assert_equal(33, pos.col) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Person |
| this.name: string |
| this.age: number = 42 |
| this.education: string = "unknown" |
| |
| def new(this.name, this.age = v:none, this.education = v:none) |
| enddef |
| endclass |
| |
| var piet = Person.new("Piet") |
| assert_equal("Piet", piet.name) |
| assert_equal(42, piet.age) |
| assert_equal("unknown", piet.education) |
| |
| var chris = Person.new("Chris", 4, "none") |
| assert_equal("Chris", chris.name) |
| assert_equal(4, chris.age) |
| assert_equal("none", chris.education) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Person |
| this.name: string |
| this.age: number = 42 |
| this.education: string = "unknown" |
| |
| def new(this.name, this.age = v:none, this.education = v:none) |
| enddef |
| endclass |
| |
| var missing = Person.new() |
| END |
| v9.CheckScriptFailure(lines, 'E119:') |
| enddef |
| |
| |
| def Test_class_new_with_object_member() |
| var lines =<< trim END |
| vim9script |
| |
| class C |
| this.str: string |
| this.num: number |
| def new(this.str, this.num) |
| enddef |
| def newVals(this.str, this.num) |
| enddef |
| endclass |
| |
| def Check() |
| try |
| var c = C.new('cats', 2) |
| assert_equal('cats', c.str) |
| assert_equal(2, c.num) |
| |
| c = C.newVals('dogs', 4) |
| assert_equal('dogs', c.str) |
| assert_equal(4, c.num) |
| catch |
| assert_report($'Unexpected exception was caught: {v:exception}') |
| endtry |
| enddef |
| |
| Check() |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class C |
| this.str: string |
| this.num: number |
| def new(this.str, this.num) |
| enddef |
| endclass |
| |
| def Check() |
| try |
| var c = C.new(1, 2) |
| catch |
| assert_report($'Unexpected exception was caught: {v:exception}') |
| endtry |
| enddef |
| |
| Check() |
| END |
| v9.CheckScriptFailure(lines, 'E1013:') |
| |
| lines =<< trim END |
| vim9script |
| |
| class C |
| this.str: string |
| this.num: number |
| def newVals(this.str, this.num) |
| enddef |
| endclass |
| |
| def Check() |
| try |
| var c = C.newVals('dogs', 'apes') |
| catch |
| assert_report($'Unexpected exception was caught: {v:exception}') |
| endtry |
| enddef |
| |
| Check() |
| END |
| v9.CheckScriptFailure(lines, 'E1013:') |
| enddef |
| |
| def Test_class_object_member_inits() |
| var lines =<< trim END |
| vim9script |
| class TextPosition |
| this.lnum: number |
| this.col = 1 |
| this.addcol: number = 2 |
| endclass |
| |
| var pos = TextPosition.new() |
| assert_equal(0, pos.lnum) |
| assert_equal(1, pos.col) |
| assert_equal(2, pos.addcol) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class TextPosition |
| this.lnum |
| this.col = 1 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1022:') |
| |
| lines =<< trim END |
| vim9script |
| class TextPosition |
| this.lnum = v:none |
| this.col = 1 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1330:') |
| |
| # Test for initializing an object member with an unknown variable/type |
| lines =<< trim END |
| vim9script |
| class A |
| this.value = init_val |
| endclass |
| END |
| v9.CheckScriptFailureList(lines, ['E121:', 'E1329:']) |
| enddef |
| |
| def Test_class_object_member_access() |
| var lines =<< trim END |
| vim9script |
| class Triple |
| this._one = 1 |
| this.two = 2 |
| public this.three = 3 |
| |
| def GetOne(): number |
| return this._one |
| enddef |
| endclass |
| |
| var trip = Triple.new() |
| assert_equal(1, trip.GetOne()) |
| assert_equal(2, trip.two) |
| assert_equal(3, trip.three) |
| assert_fails('echo trip._one', 'E1333') |
| |
| assert_fails('trip._one = 11', 'E1333') |
| assert_fails('trip.two = 22', 'E1335') |
| trip.three = 33 |
| assert_equal(33, trip.three) |
| |
| assert_fails('trip.four = 4', 'E1334') |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Test for a public member variable name beginning with an underscore |
| lines =<< trim END |
| vim9script |
| class A |
| public this._val = 10 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1332:') |
| |
| lines =<< trim END |
| vim9script |
| |
| class MyCar |
| this.make: string |
| this.age = 5 |
| |
| def new(make_arg: string) |
| this.make = make_arg |
| enddef |
| |
| def GetMake(): string |
| return $"make = {this.make}" |
| enddef |
| def GetAge(): number |
| return this.age |
| enddef |
| endclass |
| |
| var c = MyCar.new("abc") |
| assert_equal('make = abc', c.GetMake()) |
| |
| c = MyCar.new("def") |
| assert_equal('make = def', c.GetMake()) |
| |
| var c2 = MyCar.new("123") |
| assert_equal('make = 123', c2.GetMake()) |
| |
| def CheckCar() |
| assert_equal("make = def", c.GetMake()) |
| assert_equal(5, c.GetAge()) |
| enddef |
| CheckCar() |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class MyCar |
| this.make: string |
| |
| def new(make_arg: string) |
| this.make = make_arg |
| enddef |
| endclass |
| |
| var c = MyCar.new("abc") |
| var c = MyCar.new("def") |
| END |
| v9.CheckScriptFailure(lines, 'E1041:') |
| |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| this.x: list<number> = [] |
| |
| def Add(n: number): any |
| this.x->add(n) |
| return this |
| enddef |
| endclass |
| |
| echo Foo.new().Add(1).Add(2).x |
| echo Foo.new().Add(1).Add(2) |
| .x |
| echo Foo.new().Add(1) |
| .Add(2).x |
| echo Foo.new() |
| .Add(1).Add(2).x |
| echo Foo.new() |
| .Add(1) |
| .Add(2) |
| .x |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Test for "public" cannot be abbreviated |
| lines =<< trim END |
| vim9script |
| class Something |
| pub this.val = 1 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1065:') |
| |
| # Test for "public" keyword must be followed by "this" or "static". |
| lines =<< trim END |
| vim9script |
| class Something |
| public val = 1 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1331:') |
| |
| # Test for "static" cannot be abbreviated |
| lines =<< trim END |
| vim9script |
| class Something |
| stat this.val = 1 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1065:') |
| enddef |
| |
| def Test_class_object_compare() |
| var class_lines =<< trim END |
| vim9script |
| class Item |
| this.nr = 0 |
| this.name = 'xx' |
| endclass |
| END |
| |
| # used at the script level and in a compiled function |
| var test_lines =<< trim END |
| var i1 = Item.new() |
| assert_equal(i1, i1) |
| assert_true(i1 is i1) |
| var i2 = Item.new() |
| assert_equal(i1, i2) |
| assert_false(i1 is i2) |
| var i3 = Item.new(0, 'xx') |
| assert_equal(i1, i3) |
| |
| var io1 = Item.new(1, 'xx') |
| assert_notequal(i1, io1) |
| var io2 = Item.new(0, 'yy') |
| assert_notequal(i1, io2) |
| END |
| |
| v9.CheckScriptSuccess(class_lines + test_lines) |
| v9.CheckScriptSuccess( |
| class_lines + ['def Test()'] + test_lines + ['enddef', 'Test()']) |
| |
| for op in ['>', '>=', '<', '<=', '=~', '!~'] |
| var op_lines = [ |
| 'var i1 = Item.new()', |
| 'var i2 = Item.new()', |
| 'echo i1 ' .. op .. ' i2', |
| ] |
| v9.CheckScriptFailure(class_lines + op_lines, 'E1153: Invalid operation for object') |
| v9.CheckScriptFailure(class_lines |
| + ['def Test()'] + op_lines + ['enddef', 'Test()'], 'E1153: Invalid operation for object') |
| endfor |
| enddef |
| |
| def Test_object_type() |
| var lines =<< trim END |
| vim9script |
| |
| class One |
| this.one = 1 |
| endclass |
| class Two |
| this.two = 2 |
| endclass |
| class TwoMore extends Two |
| this.more = 9 |
| endclass |
| |
| var o: One = One.new() |
| var t: Two = Two.new() |
| var m: TwoMore = TwoMore.new() |
| var tm: Two = TwoMore.new() |
| |
| t = m |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class One |
| this.one = 1 |
| endclass |
| class Two |
| this.two = 2 |
| endclass |
| |
| var o: One = Two.new() |
| END |
| v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object<One> but got object<Two>') |
| |
| lines =<< trim END |
| vim9script |
| |
| interface One |
| def GetMember(): number |
| endinterface |
| class Two implements One |
| this.one = 1 |
| def GetMember(): number |
| return this.one |
| enddef |
| endclass |
| |
| var o: One = Two.new(5) |
| assert_equal(5, o.GetMember()) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class Num |
| this.n: number = 0 |
| endclass |
| |
| def Ref(name: string): func(Num): Num |
| return (arg: Num): Num => { |
| return eval(name)(arg) |
| } |
| enddef |
| |
| const Fn = Ref('Double') |
| var Double = (m: Num): Num => Num.new(m.n * 2) |
| |
| echo Fn(Num.new(4)) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_class_member() |
| # check access rules |
| var lines =<< trim END |
| vim9script |
| class TextPos |
| this.lnum = 1 |
| this.col = 1 |
| static counter = 0 |
| static _secret = 7 |
| public static anybody = 42 |
| |
| static def AddToCounter(nr: number) |
| counter += nr |
| enddef |
| endclass |
| |
| assert_equal(0, TextPos.counter) |
| TextPos.AddToCounter(3) |
| assert_equal(3, TextPos.counter) |
| assert_fails('echo TextPos.noSuchMember', 'E1338:') |
| |
| def GetCounter(): number |
| return TextPos.counter |
| enddef |
| assert_equal(3, GetCounter()) |
| |
| assert_fails('TextPos.noSuchMember = 2', 'E1337:') |
| assert_fails('TextPos.counter = 5', 'E1335:') |
| assert_fails('TextPos.counter += 5', 'E1335:') |
| |
| assert_fails('echo TextPos._secret', 'E1333:') |
| assert_fails('TextPos._secret = 8', 'E1333:') |
| |
| assert_equal(42, TextPos.anybody) |
| TextPos.anybody = 12 |
| assert_equal(12, TextPos.anybody) |
| TextPos.anybody += 5 |
| assert_equal(17, TextPos.anybody) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # example in the help |
| lines =<< trim END |
| vim9script |
| class OtherThing |
| this.size: number |
| static totalSize: number |
| |
| def new(this.size) |
| totalSize += this.size |
| enddef |
| endclass |
| assert_equal(0, OtherThing.totalSize) |
| var to3 = OtherThing.new(3) |
| assert_equal(3, OtherThing.totalSize) |
| var to7 = OtherThing.new(7) |
| assert_equal(10, OtherThing.totalSize) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # using static class member twice |
| lines =<< trim END |
| vim9script |
| |
| class HTML |
| static author: string = 'John Doe' |
| |
| static def MacroSubstitute(s: string): string |
| return substitute(s, '{{author}}', author, 'gi') |
| enddef |
| endclass |
| |
| assert_equal('some text', HTML.MacroSubstitute('some text')) |
| assert_equal('some text', HTML.MacroSubstitute('some text')) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # access private member in lambda |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| this._x: number = 0 |
| |
| def Add(n: number): number |
| const F = (): number => this._x + n |
| return F() |
| enddef |
| endclass |
| |
| var foo = Foo.new() |
| assert_equal(5, foo.Add(5)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # access private member in lambda body |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| this._x: number = 6 |
| |
| def Add(n: number): number |
| var Lam = () => { |
| this._x = this._x + n |
| } |
| Lam() |
| return this._x |
| enddef |
| endclass |
| |
| var foo = Foo.new() |
| assert_equal(13, foo.Add(7)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # check shadowing |
| lines =<< trim END |
| vim9script |
| |
| class Some |
| static count = 0 |
| def Method(count: number) |
| echo count |
| enddef |
| endclass |
| |
| var s = Some.new() |
| s.Method(7) |
| END |
| v9.CheckScriptFailure(lines, 'E1340: Argument already declared in the class: count') |
| |
| lines =<< trim END |
| vim9script |
| |
| class Some |
| static count = 0 |
| def Method(arg: number) |
| var count = 3 |
| echo arg count |
| enddef |
| endclass |
| |
| var s = Some.new() |
| s.Method(7) |
| END |
| v9.CheckScriptFailure(lines, 'E1341: Variable already declared in the class: count') |
| |
| # Test for using an invalid type for a member variable |
| lines =<< trim END |
| vim9script |
| class A |
| this.val: xxx |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1010:') |
| |
| # Test for no space before or after the '=' when initializing a member |
| # variable |
| lines =<< trim END |
| vim9script |
| class A |
| this.val: number= 10 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1004:') |
| lines =<< trim END |
| vim9script |
| class A |
| this.val: number =10 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1004:') |
| |
| # Access a non-existing member |
| lines =<< trim END |
| vim9script |
| class A |
| endclass |
| var a = A.new() |
| var v = a.bar |
| END |
| v9.CheckScriptFailure(lines, 'E1326:') |
| enddef |
| |
| func Test_class_garbagecollect() |
| let lines =<< trim END |
| vim9script |
| |
| class Point |
| this.p = [2, 3] |
| static pl = ['a', 'b'] |
| static pd = {a: 'a', b: 'b'} |
| endclass |
| |
| echo Point.pl Point.pd |
| call test_garbagecollect_now() |
| echo Point.pl Point.pd |
| END |
| call v9.CheckScriptSuccess(lines) |
| |
| let lines =<< trim END |
| vim9script |
| |
| interface View |
| endinterface |
| |
| class Widget |
| this.view: View |
| endclass |
| |
| class MyView implements View |
| this.widget: Widget |
| |
| def new() |
| # this will result in a circular reference to this object |
| this.widget = Widget.new(this) |
| enddef |
| endclass |
| |
| var view = MyView.new() |
| |
| # overwrite "view", will be garbage-collected next |
| view = MyView.new() |
| test_garbagecollect_now() |
| END |
| call v9.CheckScriptSuccess(lines) |
| endfunc |
| |
| def Test_class_function() |
| var lines =<< trim END |
| vim9script |
| class Value |
| this.value = 0 |
| static objects = 0 |
| |
| def new(v: number) |
| this.value = v |
| ++objects |
| enddef |
| |
| static def GetCount(): number |
| return objects |
| enddef |
| endclass |
| |
| assert_equal(0, Value.GetCount()) |
| var v1 = Value.new(2) |
| assert_equal(1, Value.GetCount()) |
| var v2 = Value.new(7) |
| assert_equal(2, Value.GetCount()) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Test for cleaning up after a class definition failure when using class |
| # functions. |
| lines =<< trim END |
| vim9script |
| class A |
| static def Foo() |
| enddef |
| aaa |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1318:') |
| enddef |
| |
| def Test_class_defcompile() |
| var lines =<< trim END |
| vim9script |
| |
| class C |
| def Fo(i: number): string |
| return i |
| enddef |
| endclass |
| |
| defcompile C.Fo |
| END |
| v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number') |
| |
| lines =<< trim END |
| vim9script |
| |
| class C |
| static def Fc(): number |
| return 'x' |
| enddef |
| endclass |
| |
| defcompile C.Fc |
| END |
| v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got string') |
| |
| # Trying to compile a function using a non-existing class variable |
| lines =<< trim END |
| vim9script |
| defcompile x.Foo() |
| END |
| v9.CheckScriptFailure(lines, 'E475:') |
| |
| # Trying to compile a function using a variable which is not a class |
| lines =<< trim END |
| vim9script |
| var x: number |
| defcompile x.Foo() |
| END |
| v9.CheckScriptFailure(lines, 'E475:') |
| |
| # Trying to compile a function without specifying the name |
| lines =<< trim END |
| vim9script |
| class A |
| endclass |
| defcompile A. |
| END |
| v9.CheckScriptFailure(lines, 'E475:') |
| |
| # Trying to compile a non-existing class object member function |
| lines =<< trim END |
| vim9script |
| class A |
| endclass |
| var a = A.new() |
| defcompile a.Foo() |
| END |
| v9.CheckScriptFailureList(lines, ['E1334:', 'E475:']) |
| enddef |
| |
| def Test_class_object_to_string() |
| var lines =<< trim END |
| vim9script |
| class TextPosition |
| this.lnum = 1 |
| this.col = 22 |
| endclass |
| |
| assert_equal("class TextPosition", string(TextPosition)) |
| |
| var pos = TextPosition.new() |
| assert_equal("object of TextPosition {lnum: 1, col: 22}", string(pos)) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_interface_basics() |
| var lines =<< trim END |
| vim9script |
| interface Something |
| this.value: string |
| static count: number |
| def GetCount(): number |
| endinterface |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| interface SomethingWrong |
| static count = 7 |
| endinterface |
| END |
| v9.CheckScriptFailure(lines, 'E1342:') |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| static count: number |
| def Method(count: number) |
| endinterface |
| END |
| v9.CheckScriptFailure(lines, 'E1340: Argument already declared in the class: count', 5) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| this.value: number |
| def Method(value: number) |
| endinterface |
| END |
| # The argument name and the object member name are the same, but this is not a |
| # problem because object members are always accessed with the "this." prefix. |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| interface somethingWrong |
| static count = 7 |
| endinterface |
| END |
| v9.CheckScriptFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong') |
| |
| lines =<< trim END |
| vim9script |
| interface SomethingWrong |
| this.value: string |
| static count = 7 |
| def GetCount(): number |
| endinterface |
| END |
| v9.CheckScriptFailure(lines, 'E1344:') |
| |
| lines =<< trim END |
| vim9script |
| interface SomethingWrong |
| this.value: string |
| static count: number |
| def GetCount(): number |
| return 5 |
| enddef |
| endinterface |
| END |
| v9.CheckScriptFailure(lines, 'E1345: Not a valid command in an interface: return 5') |
| |
| lines =<< trim END |
| vim9script |
| export interface EnterExit |
| def Enter(): void |
| def Exit(): void |
| endinterface |
| END |
| writefile(lines, 'XdefIntf.vim', 'D') |
| |
| lines =<< trim END |
| vim9script |
| import './XdefIntf.vim' as defIntf |
| export def With(ee: defIntf.EnterExit, F: func) |
| ee.Enter() |
| try |
| F() |
| finally |
| ee.Exit() |
| endtry |
| enddef |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| var imported =<< trim END |
| vim9script |
| export abstract class EnterExit |
| def Enter(): void |
| enddef |
| def Exit(): void |
| enddef |
| endclass |
| END |
| writefile(imported, 'XdefIntf2.vim', 'D') |
| |
| lines[1] = " import './XdefIntf2.vim' as defIntf" |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_class_implements_interface() |
| var lines =<< trim END |
| vim9script |
| |
| interface Some |
| static count: number |
| def Method(nr: number) |
| endinterface |
| |
| class SomeImpl implements Some |
| static count: number |
| def Method(nr: number) |
| echo nr |
| enddef |
| endclass |
| |
| interface Another |
| this.member: string |
| endinterface |
| |
| class AnotherImpl implements Some, Another |
| this.member = 'abc' |
| static count: number |
| def Method(nr: number) |
| echo nr |
| enddef |
| endclass |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| static counter: number |
| endinterface |
| |
| class SomeImpl implements Some implements Some |
| static count: number |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1350:') |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| static counter: number |
| endinterface |
| |
| class SomeImpl implements Some, Some |
| static count: number |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1351: Duplicate interface after "implements": Some') |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| static counter: number |
| def Method(nr: number) |
| endinterface |
| |
| class SomeImpl implements Some |
| static count: number |
| def Method(nr: number) |
| echo nr |
| enddef |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1348: Member "counter" of interface "Some" not implemented') |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| static count: number |
| def Methods(nr: number) |
| endinterface |
| |
| class SomeImpl implements Some |
| static count: number |
| def Method(nr: number) |
| echo nr |
| enddef |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1349: Function "Methods" of interface "Some" not implemented') |
| |
| # Check different order of members in class and interface works. |
| lines =<< trim END |
| vim9script |
| |
| interface Result |
| public this.label: string |
| this.errpos: number |
| endinterface |
| |
| # order of members is opposite of interface |
| class Failure implements Result |
| this.errpos: number = 42 |
| public this.label: string = 'label' |
| endclass |
| |
| def Test() |
| var result: Result = Failure.new() |
| |
| assert_equal('label', result.label) |
| assert_equal(42, result.errpos) |
| |
| result.label = 'different' |
| assert_equal('different', result.label) |
| assert_equal(42, result.errpos) |
| enddef |
| |
| Test() |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Interface name after "extends" doesn't end in a space or NUL character |
| lines =<< trim END |
| vim9script |
| interface A |
| endinterface |
| class B extends A" |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1315:') |
| |
| # Trailing characters after a class name |
| lines =<< trim END |
| vim9script |
| class A bbb |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E488:') |
| |
| # using "implements" with a non-existing class |
| lines =<< trim END |
| vim9script |
| class A implements B |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1346:') |
| |
| # using "implements" with a regular class |
| lines =<< trim END |
| vim9script |
| class A |
| endclass |
| class B implements A |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1347:') |
| |
| # using "implements" with a variable |
| lines =<< trim END |
| vim9script |
| var T: number = 10 |
| class A implements T |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1347:') |
| |
| # all the class methods in an "interface" should be implemented |
| lines =<< trim END |
| vim9script |
| interface A |
| static def Foo() |
| endinterface |
| class B implements A |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1349:') |
| enddef |
| |
| def Test_call_interface_method() |
| var lines =<< trim END |
| vim9script |
| interface Base |
| def Enter(): void |
| endinterface |
| |
| class Child implements Base |
| def Enter(): void |
| g:result ..= 'child' |
| enddef |
| endclass |
| |
| def F(obj: Base) |
| obj.Enter() |
| enddef |
| |
| g:result = '' |
| F(Child.new()) |
| assert_equal('child', g:result) |
| unlet g:result |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| def Enter(): void |
| g:result ..= 'base' |
| enddef |
| endclass |
| |
| class Child extends Base |
| def Enter(): void |
| g:result ..= 'child' |
| enddef |
| endclass |
| |
| def F(obj: Base) |
| obj.Enter() |
| enddef |
| |
| g:result = '' |
| F(Child.new()) |
| assert_equal('child', g:result) |
| unlet g:result |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # method of interface returns a value |
| lines =<< trim END |
| vim9script |
| interface Base |
| def Enter(): string |
| endinterface |
| |
| class Child implements Base |
| def Enter(): string |
| g:result ..= 'child' |
| return "/resource" |
| enddef |
| endclass |
| |
| def F(obj: Base) |
| var r = obj.Enter() |
| g:result ..= r |
| enddef |
| |
| g:result = '' |
| F(Child.new()) |
| assert_equal('child/resource', g:result) |
| unlet g:result |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| def Enter(): string |
| return null_string |
| enddef |
| endclass |
| |
| class Child extends Base |
| def Enter(): string |
| g:result ..= 'child' |
| return "/resource" |
| enddef |
| endclass |
| |
| def F(obj: Base) |
| var r = obj.Enter() |
| g:result ..= r |
| enddef |
| |
| g:result = '' |
| F(Child.new()) |
| assert_equal('child/resource', g:result) |
| unlet g:result |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| |
| # No class that implements the interface. |
| lines =<< trim END |
| vim9script |
| |
| interface IWithEE |
| def Enter(): any |
| def Exit(): void |
| endinterface |
| |
| def With1(ee: IWithEE, F: func) |
| var r = ee.Enter() |
| enddef |
| |
| defcompile |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_class_used_as_type() |
| var lines =<< trim END |
| vim9script |
| |
| class Point |
| this.x = 0 |
| this.y = 0 |
| endclass |
| |
| var p: Point |
| p = Point.new(2, 33) |
| assert_equal(2, p.x) |
| assert_equal(33, p.y) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface HasX |
| this.x: number |
| endinterface |
| |
| class Point implements HasX |
| this.x = 0 |
| this.y = 0 |
| endclass |
| |
| var p: Point |
| p = Point.new(2, 33) |
| var hx = p |
| assert_equal(2, hx.x) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class Point |
| this.x = 0 |
| this.y = 0 |
| endclass |
| |
| var p: Point |
| p = 'text' |
| END |
| v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object<Point> but got string') |
| enddef |
| |
| def Test_class_extends() |
| var lines =<< trim END |
| vim9script |
| class Base |
| this.one = 1 |
| def GetOne(): number |
| return this.one |
| enddef |
| endclass |
| class Child extends Base |
| this.two = 2 |
| def GetTotal(): number |
| return this.one + this.two |
| enddef |
| endclass |
| var o = Child.new() |
| assert_equal(1, o.one) |
| assert_equal(2, o.two) |
| assert_equal(1, o.GetOne()) |
| assert_equal(3, o.GetTotal()) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| this.one = 1 |
| endclass |
| class Child extends Base |
| this.two = 2 |
| endclass |
| var o = Child.new(3, 44) |
| assert_equal(3, o.one) |
| assert_equal(44, o.two) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| this.one = 1 |
| endclass |
| class Child extends Base extends Base |
| this.two = 2 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1352: Duplicate "extends"') |
| |
| lines =<< trim END |
| vim9script |
| class Child extends BaseClass |
| this.two = 2 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1353: Class name not found: BaseClass') |
| |
| lines =<< trim END |
| vim9script |
| var SomeVar = 99 |
| class Child extends SomeVar |
| this.two = 2 |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1354: Cannot extend SomeVar') |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| this.name: string |
| def ToString(): string |
| return this.name |
| enddef |
| endclass |
| |
| class Child extends Base |
| this.age: number |
| def ToString(): string |
| return super.ToString() .. ': ' .. this.age |
| enddef |
| endclass |
| |
| var o = Child.new('John', 42) |
| assert_equal('John: 42', o.ToString()) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Child |
| this.age: number |
| def ToString(): number |
| return this.age |
| enddef |
| def ToString(): string |
| return this.age |
| enddef |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1355: Duplicate function: ToString') |
| |
| lines =<< trim END |
| vim9script |
| class Child |
| this.age: number |
| def ToString(): string |
| return super .ToString() .. ': ' .. this.age |
| enddef |
| endclass |
| var o = Child.new(42) |
| echo o.ToString() |
| END |
| v9.CheckScriptFailure(lines, 'E1356:') |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| this.name: string |
| def ToString(): string |
| return this.name |
| enddef |
| endclass |
| |
| var age = 42 |
| def ToString(): string |
| return super.ToString() .. ': ' .. age |
| enddef |
| echo ToString() |
| END |
| v9.CheckScriptFailure(lines, 'E1357:') |
| |
| lines =<< trim END |
| vim9script |
| class Child |
| this.age: number |
| def ToString(): string |
| return super.ToString() .. ': ' .. this.age |
| enddef |
| endclass |
| var o = Child.new(42) |
| echo o.ToString() |
| END |
| v9.CheckScriptFailure(lines, 'E1358:') |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| this.name: string |
| static def ToString(): string |
| return 'Base class' |
| enddef |
| endclass |
| |
| class Child extends Base |
| this.age: number |
| def ToString(): string |
| return Base.ToString() .. ': ' .. this.age |
| enddef |
| endclass |
| |
| var o = Child.new('John', 42) |
| assert_equal('Base class: 42', o.ToString()) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| this.value = 1 |
| def new(init: number) |
| this.value = number + 1 |
| enddef |
| endclass |
| class Child extends Base |
| def new() |
| this.new(3) |
| enddef |
| endclass |
| var c = Child.new() |
| END |
| v9.CheckScriptFailure(lines, 'E1325: Method not found on class "Child": new(') |
| |
| # base class with more than one object member |
| lines =<< trim END |
| vim9script |
| |
| class Result |
| this.success: bool |
| this.value: any = null |
| endclass |
| |
| class Success extends Result |
| def new(this.value = v:none) |
| this.success = true |
| enddef |
| endclass |
| |
| var v = Success.new('asdf') |
| assert_equal("object of Success {success: true, value: 'asdf'}", string(v)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # class name after "extends" doesn't end in a space or NUL character |
| lines =<< trim END |
| vim9script |
| class A |
| endclass |
| class B extends A" |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1315:') |
| enddef |
| |
| def Test_using_base_class() |
| var lines =<< trim END |
| vim9script |
| |
| class BaseEE |
| def Enter(): any |
| return null |
| enddef |
| def Exit(resource: any): void |
| enddef |
| endclass |
| |
| class ChildEE extends BaseEE |
| def Enter(): any |
| return 42 |
| enddef |
| |
| def Exit(resource: number): void |
| g:result ..= '/exit' |
| enddef |
| endclass |
| |
| def With(ee: BaseEE) |
| var r = ee.Enter() |
| try |
| g:result ..= r |
| finally |
| g:result ..= '/finally' |
| ee.Exit(r) |
| endtry |
| enddef |
| |
| g:result = '' |
| With(ChildEE.new()) |
| assert_equal('42/finally/exit', g:result) |
| END |
| v9.CheckScriptSuccess(lines) |
| unlet g:result |
| |
| # Using super, Child invokes Base method which has optional arg. #12471 |
| lines =<< trim END |
| vim9script |
| |
| class Base |
| this.success: bool = false |
| def Method(arg = 0) |
| this.success = true |
| enddef |
| endclass |
| |
| class Child extends Base |
| def new() |
| super.Method() |
| enddef |
| endclass |
| |
| var obj = Child.new() |
| assert_equal(true, obj.success) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| |
| def Test_class_import() |
| var lines =<< trim END |
| vim9script |
| export class Animal |
| this.kind: string |
| this.name: string |
| endclass |
| END |
| writefile(lines, 'Xanimal.vim', 'D') |
| |
| lines =<< trim END |
| vim9script |
| import './Xanimal.vim' as animal |
| |
| var a: animal.Animal |
| a = animal.Animal.new('fish', 'Eric') |
| assert_equal('fish', a.kind) |
| assert_equal('Eric', a.name) |
| |
| var b: animal.Animal = animal.Animal.new('cat', 'Garfield') |
| assert_equal('cat', b.kind) |
| assert_equal('Garfield', b.name) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_abstract_class() |
| var lines =<< trim END |
| vim9script |
| abstract class Base |
| this.name: string |
| endclass |
| class Person extends Base |
| this.age: number |
| endclass |
| var p: Base = Person.new('Peter', 42) |
| assert_equal('Peter', p.name) |
| assert_equal(42, p.age) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| abstract class Base |
| this.name: string |
| endclass |
| class Person extends Base |
| this.age: number |
| endclass |
| var p = Base.new('Peter') |
| END |
| v9.CheckScriptFailure(lines, 'E1325: Method not found on class "Base": new(') |
| |
| lines =<< trim END |
| abstract class Base |
| this.name: string |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1316:') |
| |
| # Abstract class cannot have a "new" function |
| lines =<< trim END |
| vim9script |
| abstract class Base |
| def new() |
| enddef |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1359:') |
| enddef |
| |
| def Test_closure_in_class() |
| var lines =<< trim END |
| vim9script |
| |
| class Foo |
| this.y: list<string> = ['B'] |
| |
| def new() |
| g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1) |
| enddef |
| endclass |
| |
| Foo.new() |
| assert_equal(['A'], g:result) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_call_constructor_from_legacy() |
| var lines =<< trim END |
| vim9script |
| |
| var newCalled = 'false' |
| |
| class A |
| def new() |
| newCalled = 'true' |
| enddef |
| endclass |
| |
| export def F(options = {}): any |
| return A |
| enddef |
| |
| g:p = F() |
| legacy call p.new() |
| assert_equal('true', newCalled) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_defer_with_object() |
| var lines =<< trim END |
| vim9script |
| |
| class CWithEE |
| def Enter() |
| g:result ..= "entered/" |
| enddef |
| def Exit() |
| g:result ..= "exited" |
| enddef |
| endclass |
| |
| def With(ee: CWithEE, F: func) |
| ee.Enter() |
| defer ee.Exit() |
| F() |
| enddef |
| |
| g:result = '' |
| var obj = CWithEE.new() |
| obj->With(() => { |
| g:result ..= "called/" |
| }) |
| assert_equal('entered/called/exited', g:result) |
| END |
| v9.CheckScriptSuccess(lines) |
| unlet g:result |
| |
| lines =<< trim END |
| vim9script |
| |
| class BaseWithEE |
| def Enter() |
| g:result ..= "entered-base/" |
| enddef |
| def Exit() |
| g:result ..= "exited-base" |
| enddef |
| endclass |
| |
| class CWithEE extends BaseWithEE |
| def Enter() |
| g:result ..= "entered-child/" |
| enddef |
| def Exit() |
| g:result ..= "exited-child" |
| enddef |
| endclass |
| |
| def With(ee: BaseWithEE, F: func) |
| ee.Enter() |
| defer ee.Exit() |
| F() |
| enddef |
| |
| g:result = '' |
| var obj = CWithEE.new() |
| obj->With(() => { |
| g:result ..= "called/" |
| }) |
| assert_equal('entered-child/called/exited-child', g:result) |
| END |
| v9.CheckScriptSuccess(lines) |
| unlet g:result |
| enddef |
| |
| " The following test used to crash Vim (Github issue #12676) |
| def Test_extends_method_crashes_vim() |
| var lines =<< trim END |
| vim9script |
| |
| class Observer |
| endclass |
| |
| class Property |
| this.value: any |
| |
| def Set(v: any) |
| if v != this.value |
| this.value = v |
| endif |
| enddef |
| |
| def Register(observer: Observer) |
| enddef |
| endclass |
| |
| class Bool extends Property |
| this.value: bool |
| endclass |
| |
| def Observe(obj: Property, who: Observer) |
| obj.Register(who) |
| enddef |
| |
| var p = Bool.new(false) |
| var myObserver = Observer.new() |
| |
| Observe(p, myObserver) |
| |
| p.Set(true) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for calling a method in a class that is extended |
| def Test_call_method_in_extended_class() |
| var lines =<< trim END |
| vim9script |
| |
| var prop_init_called = false |
| var prop_register_called = false |
| |
| class Property |
| def Init() |
| prop_init_called = true |
| enddef |
| |
| def Register() |
| prop_register_called = true |
| enddef |
| endclass |
| |
| class Bool extends Property |
| endclass |
| |
| def Observe(obj: Property) |
| obj.Register() |
| enddef |
| |
| var p = Property.new() |
| Observe(p) |
| |
| p.Init() |
| assert_true(prop_init_called) |
| assert_true(prop_register_called) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for calling a method in the parent class that is extended partially. |
| " This used to fail with the 'E118: Too many arguments for function: Text' error |
| " message (Github issue #12524). |
| def Test_call_method_in_parent_class() |
| var lines =<< trim END |
| vim9script |
| |
| class Widget |
| this._lnum: number = 1 |
| |
| def SetY(lnum: number) |
| this._lnum = lnum |
| enddef |
| |
| def Text(): string |
| return '' |
| enddef |
| endclass |
| |
| class Foo extends Widget |
| def Text(): string |
| return '<Foo>' |
| enddef |
| endclass |
| |
| def Stack(w1: Widget, w2: Widget): list<Widget> |
| w1.SetY(1) |
| w2.SetY(2) |
| return [w1, w2] |
| enddef |
| |
| var foo1 = Foo.new() |
| var foo2 = Foo.new() |
| var l = Stack(foo1, foo2) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for calling methods from three levels of classes |
| def Test_multi_level_method_call() |
| var lines =<< trim END |
| vim9script |
| |
| var A_func1: number = 0 |
| var A_func2: number = 0 |
| var A_func3: number = 0 |
| var B_func2: number = 0 |
| var B_func3: number = 0 |
| var C_func3: number = 0 |
| |
| class A |
| def Func1() |
| A_func1 += 1 |
| enddef |
| |
| def Func2() |
| A_func2 += 1 |
| enddef |
| |
| def Func3() |
| A_func3 += 1 |
| enddef |
| endclass |
| |
| class B extends A |
| def Func2() |
| B_func2 += 1 |
| enddef |
| |
| def Func3() |
| B_func3 += 1 |
| enddef |
| endclass |
| |
| class C extends B |
| def Func3() |
| C_func3 += 1 |
| enddef |
| endclass |
| |
| def A_CallFuncs(a: A) |
| a.Func1() |
| a.Func2() |
| a.Func3() |
| enddef |
| |
| def B_CallFuncs(b: B) |
| b.Func1() |
| b.Func2() |
| b.Func3() |
| enddef |
| |
| def C_CallFuncs(c: C) |
| c.Func1() |
| c.Func2() |
| c.Func3() |
| enddef |
| |
| var cobj = C.new() |
| A_CallFuncs(cobj) |
| B_CallFuncs(cobj) |
| C_CallFuncs(cobj) |
| assert_equal(3, A_func1) |
| assert_equal(0, A_func2) |
| assert_equal(0, A_func3) |
| assert_equal(3, B_func2) |
| assert_equal(0, B_func3) |
| assert_equal(3, C_func3) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for using members from three levels of classes |
| def Test_multi_level_member_access() |
| var lines =<< trim END |
| vim9script |
| |
| class A |
| this.val1: number = 0 |
| this.val2: number = 0 |
| this.val3: number = 0 |
| endclass |
| |
| class B extends A |
| this.val2: number = 0 |
| this.val3: number = 0 |
| endclass |
| |
| class C extends B |
| this.val3: number = 0 |
| endclass |
| |
| def A_members(a: A) |
| a.val1 += 1 |
| a.val2 += 1 |
| a.val3 += 1 |
| enddef |
| |
| def B_members(b: B) |
| b.val1 += 1 |
| b.val2 += 1 |
| b.val3 += 1 |
| enddef |
| |
| def C_members(c: C) |
| c.val1 += 1 |
| c.val2 += 1 |
| c.val3 += 1 |
| enddef |
| |
| var cobj = C.new() |
| A_members(cobj) |
| B_members(cobj) |
| C_members(cobj) |
| assert_equal(3, cobj.val1) |
| assert_equal(3, cobj.val2) |
| assert_equal(3, cobj.val3) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker |