mirror of
https://github.com/vim/vim
synced 2025-03-23 02:05:11 +01:00
patch 9.0.2167: Vim9: not consistently using :var for declarations
Problem: Vim9-script object/class variable declarations use syntax that is inconsistent with the rest of the language. Solution: Use :var to declare object and class variables. closes: #13670 Signed-off-by: Doug Kearns <dougkearns@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
parent
549f8c0b4e
commit
74da0ee0a2
8 changed files with 714 additions and 607 deletions
|
@ -78,8 +78,8 @@ Let's start with a simple example: a class that stores a text position (see
|
|||
below for how to do this more efficiently): >
|
||||
|
||||
class TextPosition
|
||||
this.lnum: number
|
||||
this.col: number
|
||||
var lnum: number
|
||||
var col: number
|
||||
|
||||
def new(lnum: number, col: number)
|
||||
this.lnum = lnum
|
||||
|
@ -156,8 +156,8 @@ On the other hand, if you do not want the object variables to be read directly
|
|||
from outside the class or its sub-classes, you can make them protected. This
|
||||
is done by prefixing an underscore to the name: >
|
||||
|
||||
this._lnum: number
|
||||
this._col number
|
||||
var _lnum: number
|
||||
var _col number
|
||||
|
||||
Now you need to provide methods to get the value of the protected variables.
|
||||
These are commonly called getters. We recommend using a name that starts with
|
||||
|
@ -209,8 +209,8 @@ Many constructors take values for the object variables. Thus you very often
|
|||
see this pattern: >
|
||||
|
||||
class SomeClass
|
||||
this.lnum: number
|
||||
this.col: number
|
||||
var lnum: number
|
||||
var col: number
|
||||
|
||||
def new(lnum: number, col: number)
|
||||
this.lnum = lnum
|
||||
|
@ -235,8 +235,8 @@ Putting together this way of using new() and making the variables public
|
|||
results in a much shorter class definition than what we started with: >
|
||||
|
||||
class TextPosition
|
||||
public this.lnum: number
|
||||
public this.col: number
|
||||
public var lnum: number
|
||||
public var col: number
|
||||
|
||||
def new(this.lnum, this.col)
|
||||
enddef
|
||||
|
@ -277,8 +277,8 @@ Class members are declared with "static". They are used by the name without a
|
|||
prefix in the class where they are defined: >
|
||||
|
||||
class OtherThing
|
||||
this.size: number
|
||||
static totalSize: number
|
||||
var size: number
|
||||
static var totalSize: number
|
||||
|
||||
def new(this.size)
|
||||
totalSize += this.size
|
||||
|
@ -297,9 +297,9 @@ underscore as the first character in the name, and it can be made public by
|
|||
prefixing "public": >
|
||||
|
||||
class OtherThing
|
||||
static total: number # anybody can read, only class can write
|
||||
static _sum: number # only class can read and write
|
||||
public static result: number # anybody can read and write
|
||||
static var total: number # anybody can read, only class can write
|
||||
static var _sum: number # only class can read and write
|
||||
public static var result: number # anybody can read and write
|
||||
endclass
|
||||
<
|
||||
*class-method*
|
||||
|
@ -308,8 +308,8 @@ variables but they have no access to the object variables, they cannot use the
|
|||
"this" keyword:
|
||||
>
|
||||
class OtherThing
|
||||
this.size: number
|
||||
static totalSize: number
|
||||
var size: number
|
||||
static var totalSize: number
|
||||
|
||||
# Clear the total size and return the value it had before.
|
||||
static def ClearTotalSize(): number
|
||||
|
@ -345,14 +345,14 @@ outside of the defining class: >
|
|||
|
||||
vim9script
|
||||
class Vehicle
|
||||
static nextID: number = 1000
|
||||
static var nextID: number = 1000
|
||||
static def GetID(): number
|
||||
nextID += 1
|
||||
return nextID
|
||||
enddef
|
||||
endclass
|
||||
class Car extends Vehicle
|
||||
this.myID: number
|
||||
var myID: number
|
||||
def new()
|
||||
this.myID = Vehicle.GetID()
|
||||
enddef
|
||||
|
@ -380,20 +380,20 @@ it is. The Shape class functions as the base for a Square and a Triangle
|
|||
class, for which objects can be created. Example: >
|
||||
|
||||
abstract class Shape
|
||||
this.color = Color.Black
|
||||
this.thickness = 10
|
||||
var color = Color.Black
|
||||
var thickness = 10
|
||||
endclass
|
||||
|
||||
class Square extends Shape
|
||||
this.size: number
|
||||
var size: number
|
||||
|
||||
def new(this.size)
|
||||
enddef
|
||||
endclass
|
||||
|
||||
class Triangle extends Shape
|
||||
this.base: number
|
||||
this.height: number
|
||||
var base: number
|
||||
var height: number
|
||||
|
||||
def new(this.base, this.height)
|
||||
enddef
|
||||
|
@ -430,8 +430,8 @@ interface called HasSurface, which specifies one method Surface() that returns
|
|||
a number. This example extends the one above: >
|
||||
|
||||
abstract class Shape
|
||||
this.color = Color.Black
|
||||
this.thickness = 10
|
||||
var color = Color.Black
|
||||
var thickness = 10
|
||||
endclass
|
||||
|
||||
interface HasSurface
|
||||
|
@ -439,7 +439,7 @@ a number. This example extends the one above: >
|
|||
endinterface
|
||||
|
||||
class Square extends Shape implements HasSurface
|
||||
this.size: number
|
||||
var size: number
|
||||
|
||||
def new(this.size)
|
||||
enddef
|
||||
|
@ -450,8 +450,8 @@ a number. This example extends the one above: >
|
|||
endclass
|
||||
|
||||
class Triangle extends Shape implements HasSurface
|
||||
this.base: number
|
||||
this.height: number
|
||||
var base: number
|
||||
var height: number
|
||||
|
||||
def new(this.base, this.height)
|
||||
enddef
|
||||
|
@ -598,13 +598,13 @@ Items in a class ~
|
|||
*E1318* *E1325* *E1388*
|
||||
Inside a class, in between `:class` and `:endclass`, these items can appear:
|
||||
- An object variable declaration: >
|
||||
this._protectedVariableName: memberType
|
||||
this.readonlyVariableName: memberType
|
||||
public this.readwriteVariableName: memberType
|
||||
var _protectedVariableName: memberType
|
||||
var readonlyVariableName: memberType
|
||||
public var readwriteVariableName: memberType
|
||||
- A class variable declaration: >
|
||||
static _protectedClassVariableName: memberType
|
||||
static readonlyClassVariableName: memberType
|
||||
static public readwriteClassVariableName: memberType
|
||||
static var _protectedClassVariableName: memberType
|
||||
static var readonlyClassVariableName: memberType
|
||||
static var public readwriteClassVariableName: memberType
|
||||
- A constructor method: >
|
||||
def new(arguments)
|
||||
def newName(arguments)
|
||||
|
@ -620,9 +620,9 @@ this explicitly with ": {type}". For simple types you can also use an
|
|||
initializer, such as "= 123", and Vim will see that the type is a number.
|
||||
Avoid doing this for more complex types and when the type will be incomplete.
|
||||
For example: >
|
||||
this.nameList = []
|
||||
var nameList = []
|
||||
This specifies a list, but the item type is unknown. Better use: >
|
||||
this.nameList: list<string>
|
||||
var nameList: list<string>
|
||||
The initialization isn't needed, the list is empty by default.
|
||||
*E1330*
|
||||
Some types cannot be used, such as "void", "null" and "v:none".
|
||||
|
@ -646,7 +646,7 @@ An interface can declare methods with `:def`, including the arguments and
|
|||
return type, but without the body and without `:enddef`. Example: >
|
||||
|
||||
interface HasSurface
|
||||
this.size: number
|
||||
var size: number
|
||||
def Surface(): number
|
||||
endinterface
|
||||
|
||||
|
@ -674,9 +674,9 @@ defined. This default constructor will have arguments for all the object
|
|||
variables, in the order they were specified. Thus if your class looks like: >
|
||||
|
||||
class AutoNew
|
||||
this.name: string
|
||||
this.age: number
|
||||
this.gender: Gender
|
||||
var name: string
|
||||
var age: number
|
||||
var gender: Gender
|
||||
endclass
|
||||
|
||||
Then the default constructor will be: >
|
||||
|
@ -690,8 +690,8 @@ value for the object variables will be used. This is a more useful example,
|
|||
with default values: >
|
||||
|
||||
class TextPosition
|
||||
this.lnum: number = 1
|
||||
this.col: number = 1
|
||||
var lnum: number = 1
|
||||
var col: number = 1
|
||||
endclass
|
||||
|
||||
If you want the constructor to have mandatory arguments, you need to write it
|
||||
|
@ -947,26 +947,26 @@ Following that Vim object variables could be declared like this: >
|
|||
Some users pointed out that this looks more like an assignment than a
|
||||
declaration. Adding "var" changes that: >
|
||||
class Point
|
||||
var this.x: number
|
||||
var this.y = 0
|
||||
var x: number
|
||||
var y = 0
|
||||
endclass
|
||||
|
||||
We also need to be able to declare class variables using the "static" keyword.
|
||||
There we can also choose to leave out "var": >
|
||||
class Point
|
||||
var this.x: number
|
||||
var x: number
|
||||
static count = 0
|
||||
endclass
|
||||
|
||||
Or do use it, before "static": >
|
||||
class Point
|
||||
var this.x: number
|
||||
var x: number
|
||||
var static count = 0
|
||||
endclass
|
||||
|
||||
Or after "static": >
|
||||
class Point
|
||||
var this.x: number
|
||||
var x: number
|
||||
static var count = 0
|
||||
endclass
|
||||
|
||||
|
@ -974,9 +974,16 @@ This is more in line with "static def Func()".
|
|||
|
||||
There is no clear preference whether to use "var" or not. The two main
|
||||
reasons to leave it out are:
|
||||
1. TypeScript, Java and other popular languages do not use it.
|
||||
1. TypeScript and other popular languages do not use it.
|
||||
2. Less clutter.
|
||||
|
||||
However, it is more common for languages to reuse their general variable and
|
||||
function declaration syntax for class/object variables and methods. Vim9 also
|
||||
reuses the general function declaration syntax for methods. So, for the sake
|
||||
of consistency, we require "var" in these declarations.
|
||||
|
||||
This also allows for a natural use of "final" and "const" in the future.
|
||||
|
||||
|
||||
Using "ClassName.new()" to construct an object ~
|
||||
|
||||
|
|
11
src/errors.h
11
src/errors.h
|
@ -3402,11 +3402,12 @@ EXTERN char e_object_required_found_str[]
|
|||
INIT(= N_("E1327: Object required, found %s"));
|
||||
EXTERN char e_constructor_default_value_must_be_vnone_str[]
|
||||
INIT(= N_("E1328: Constructor default value must be v:none: %s"));
|
||||
// E1329 unused
|
||||
EXTERN char e_invalid_class_variable_declaration_str[]
|
||||
INIT(= N_("E1329: Invalid class variable declaration: %s"));
|
||||
EXTERN char e_invalid_type_for_object_variable_str[]
|
||||
INIT(= N_("E1330: Invalid type for object variable: %s"));
|
||||
EXTERN char e_public_must_be_followed_by_this_or_static[]
|
||||
INIT(= N_("E1331: Public must be followed by \"this\" or \"static\""));
|
||||
EXTERN char e_public_must_be_followed_by_var_or_static[]
|
||||
INIT(= N_("E1331: Public must be followed by \"var\" or \"static\""));
|
||||
EXTERN char e_public_variable_name_cannot_start_with_underscore_str[]
|
||||
INIT(= N_("E1332: Public variable name cannot start with underscore: %s"));
|
||||
EXTERN char e_cannot_access_protected_variable_str[]
|
||||
|
@ -3487,8 +3488,8 @@ EXTERN char e_cannot_access_protected_method_str[]
|
|||
INIT(= N_("E1366: Cannot access protected method: %s"));
|
||||
EXTERN char e_variable_str_of_interface_str_has_different_access[]
|
||||
INIT(= N_("E1367: Access level of variable \"%s\" of interface \"%s\" is different"));
|
||||
EXTERN char e_static_cannot_be_followed_by_this[]
|
||||
INIT(= N_("E1368: Static cannot be followed by \"this\" in a variable name"));
|
||||
EXTERN char e_static_must_be_followed_by_var_or_def[]
|
||||
INIT(= N_("E1368: Static must be followed by \"var\" or \"def\""));
|
||||
EXTERN char e_duplicate_variable_str[]
|
||||
INIT(= N_("E1369: Duplicate variable: %s"));
|
||||
EXTERN char e_cannot_define_new_method_as_static[]
|
||||
|
|
|
@ -4845,7 +4845,7 @@ def Test_values()
|
|||
vim9script
|
||||
|
||||
class Foo
|
||||
this.val: number
|
||||
var val: number
|
||||
def Add()
|
||||
echo this.val
|
||||
enddef
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3062,15 +3062,15 @@ def Test_disassemble_interface_static_member()
|
|||
var lines =<< trim END
|
||||
vim9script
|
||||
interface I
|
||||
this.o_var: number
|
||||
this.o_var2: number
|
||||
var o_var: number
|
||||
var o_var2: number
|
||||
endinterface
|
||||
|
||||
class C implements I
|
||||
public static s_var: number
|
||||
this.o_var: number
|
||||
public static s_var2: number
|
||||
this.o_var2: number
|
||||
public static var s_var: number
|
||||
var o_var: number
|
||||
public static var s_var2: number
|
||||
var o_var2: number
|
||||
endclass
|
||||
|
||||
def F1(i: I)
|
||||
|
@ -3124,7 +3124,7 @@ def Test_disassemble_class_variable()
|
|||
vim9script
|
||||
|
||||
class A
|
||||
public static val = 10
|
||||
public static var val = 10
|
||||
def Foo(): number
|
||||
val = 20
|
||||
return val
|
||||
|
@ -3173,7 +3173,7 @@ def Test_disassemble_ifargnotset()
|
|||
var lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
this.val: number = 10
|
||||
var val: number = 10
|
||||
endclass
|
||||
g:instr = execute('disassemble A.new')
|
||||
END
|
||||
|
|
|
@ -393,7 +393,7 @@ def Test_typealias_import()
|
|||
lines =<< trim END
|
||||
vim9script
|
||||
export class MyClass
|
||||
this.val = 10
|
||||
var val = 10
|
||||
endclass
|
||||
END
|
||||
writefile(lines, 'Xtypeexport4.vim', 'D')
|
||||
|
@ -537,7 +537,7 @@ def Test_typealias_class()
|
|||
var lines =<< trim END
|
||||
vim9script
|
||||
class C
|
||||
this.color = 'green'
|
||||
var color = 'green'
|
||||
endclass
|
||||
type MyClass = C
|
||||
var o: MyClass = MyClass.new()
|
||||
|
|
|
@ -704,6 +704,8 @@ static char *(features[]) =
|
|||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
2167,
|
||||
/**/
|
||||
2166,
|
||||
/**/
|
||||
|
|
|
@ -1558,9 +1558,9 @@ early_ret:
|
|||
has_public = TRUE;
|
||||
p = skipwhite(line + 6);
|
||||
|
||||
if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
|
||||
if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0)
|
||||
{
|
||||
emsg(_(e_public_must_be_followed_by_this_or_static));
|
||||
emsg(_(e_public_must_be_followed_by_var_or_static));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1615,30 +1615,39 @@ early_ret:
|
|||
}
|
||||
has_static = TRUE;
|
||||
p = skipwhite(ps + 6);
|
||||
|
||||
if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0)
|
||||
{
|
||||
emsg(_(e_static_must_be_followed_by_var_or_def));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// object members (public, read access, private):
|
||||
// "this._varname"
|
||||
// "this.varname"
|
||||
// "public this.varname"
|
||||
if (STRNCMP(p, "this", 4) == 0)
|
||||
// "var _varname"
|
||||
// "var varname"
|
||||
// "public var varname"
|
||||
// class members (public, read access, private):
|
||||
// "static var _varname"
|
||||
// "static var varname"
|
||||
// "public static var varname"
|
||||
if (checkforcmd(&p, "var", 3))
|
||||
{
|
||||
if (p[4] != '.' || !eval_isnamec1(p[5]))
|
||||
{
|
||||
semsg(_(e_invalid_object_variable_declaration_str), p);
|
||||
break;
|
||||
}
|
||||
if (has_static)
|
||||
{
|
||||
emsg(_(e_static_cannot_be_followed_by_this));
|
||||
break;
|
||||
}
|
||||
char_u *varname = p + 5;
|
||||
char_u *varname = p;
|
||||
char_u *varname_end = NULL;
|
||||
type_T *type = NULL;
|
||||
char_u *init_expr = NULL;
|
||||
int has_type = FALSE;
|
||||
|
||||
if (!eval_isnamec1(*p))
|
||||
{
|
||||
if (has_static)
|
||||
semsg(_(e_invalid_class_variable_declaration_str), line);
|
||||
else
|
||||
semsg(_(e_invalid_object_variable_declaration_str), line);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_class && *varname == '_')
|
||||
{
|
||||
// private variables are not supported in an interface
|
||||
|
@ -1662,7 +1671,7 @@ early_ret:
|
|||
vim_free(init_expr);
|
||||
break;
|
||||
}
|
||||
if (add_member(&objmembers, varname, varname_end,
|
||||
if (add_member(has_static ? &classmembers : &objmembers, varname, varname_end,
|
||||
has_public, has_type, type, init_expr) == FAIL)
|
||||
{
|
||||
vim_free(init_expr);
|
||||
|
@ -1764,42 +1773,6 @@ early_ret:
|
|||
}
|
||||
}
|
||||
|
||||
// class members
|
||||
else if (has_static)
|
||||
{
|
||||
// class members (public, read access, private):
|
||||
// "static _varname"
|
||||
// "static varname"
|
||||
// "public static varname"
|
||||
char_u *varname = p;
|
||||
char_u *varname_end = NULL;
|
||||
int has_type = FALSE;
|
||||
type_T *type = NULL;
|
||||
char_u *init_expr = NULL;
|
||||
|
||||
if (parse_member(eap, line, varname, has_public,
|
||||
&varname_end, &has_type, &type_list, &type,
|
||||
&init_expr) == FAIL)
|
||||
break;
|
||||
if (is_reserved_varname(varname, varname_end))
|
||||
{
|
||||
vim_free(init_expr);
|
||||
break;
|
||||
}
|
||||
if (is_duplicate_variable(&classmembers, &objmembers, varname,
|
||||
varname_end))
|
||||
{
|
||||
vim_free(init_expr);
|
||||
break;
|
||||
}
|
||||
if (add_member(&classmembers, varname, varname_end,
|
||||
has_public, has_type, type, init_expr) == FAIL)
|
||||
{
|
||||
vim_free(init_expr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (is_class)
|
||||
|
|
Loading…
Add table
Reference in a new issue