]> granicus.if.org Git - vim/commitdiff
patch 9.0.1001: classes are not documented or implemented yet v9.0.1001
authorBram Moolenaar <Bram@vim.org>
Sun, 4 Dec 2022 20:13:24 +0000 (20:13 +0000)
committerBram Moolenaar <Bram@vim.org>
Sun, 4 Dec 2022 20:13:24 +0000 (20:13 +0000)
Problem:    Classes are not documented or implemented yet.
Solution:   Make the first steps at documenting Vim9 objects, classes and
            interfaces.  Make initial choices for the syntax.  Add a skeleton
            implementation.  Add "public" and "this" in the command table.

18 files changed:
Filelist
runtime/doc/Makefile
runtime/doc/vim9.txt
runtime/doc/vim9class.txt [new file with mode: 0644]
src/Make_ami.mak
src/Make_cyg_ming.mak
src/Make_mvc.mak
src/Make_vms.mms
src/Makefile
src/errors.h
src/ex_cmdidxs.h
src/ex_cmds.h
src/ex_docmd.c
src/proto.h
src/proto/vim9class.pro [new file with mode: 0644]
src/testdir/test_cmdmods.vim
src/version.c
src/vim9class.c [new file with mode: 0644]

index e241e17ec774c510aeadc9375b2ab7d1b2b34984..385ec68a71d2f1a2d6a01cd5993fbe48958bed0d 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -163,6 +163,7 @@ SRC_ALL =   \
                src/version.h \
                src/vim.h \
                src/vim9.h \
+               src/vim9class.c \
                src/vim9cmds.c \
                src/vim9compile.c \
                src/vim9execute.c \
@@ -327,6 +328,7 @@ SRC_ALL =   \
                src/proto/usercmd.pro \
                src/proto/userfunc.pro \
                src/proto/version.pro \
+               src/proto/vim9class.pro \
                src/proto/vim9cmds.pro \
                src/proto/vim9compile.pro \
                src/proto/vim9execute.pro \
index 5786799718f4f4b8fbd752801e71d0e311d986b0..2ac337a5a2760b7fa1c7f63ffff16dc6e3cedcf4 100644 (file)
@@ -161,6 +161,7 @@ DOCS = \
        version9.txt \
        vi_diff.txt \
        vim9.txt \
+       vim9class.txt \
        visual.txt \
        windows.txt \
        workshop.txt
@@ -313,6 +314,7 @@ HTMLS = \
        vi_diff.html \
        vimindex.html \
        vim9.html \
+       vim9class.html \
        visual.html \
        windows.html \
        workshop.html
index d7f78087aa218c334078087f31e1945e57932dba..1b1298cd8c8348fbdff4762f2a973d7b09a2cf61 100644 (file)
@@ -16,7 +16,7 @@ features in Vim9 script.
 3.  New style functions                        |fast-functions|
 4.  Types                              |vim9-types|
 5.  Namespace, Import and Export       |vim9script|
-6.  Future work: classes               |vim9-classes|
+6.  Classes and interfaces             |vim9-classes|
 
 9.  Rationale                          |vim9-rationale|
 
@@ -1940,73 +1940,17 @@ Or: >
 
 ==============================================================================
 
-6. Future work: classes                                        *vim9-classes*
-
-Above "class" was mentioned a few times, but it has not been implemented yet.
-Most of Vim9 script can be created without this functionality, and since
-implementing classes is going to be a lot of work, it is left for the future.
-For now we'll just make sure classes can be added later.
-
-Thoughts:
-- `class` / `endclass`, the whole class must be in one file
-- Class names are always CamelCase (to avoid a name clash with builtin types)
-- A single constructor called "constructor" (similar to TypeScript)
-- Single inheritance: `class ThisClass extends BaseClass`
-- `interface` / `endinterface` (looks like a class without any implementation)
-- Explicit declaration that the class supports an interface, so that type
-  checking works properly:
-  `class SomeClass implements SomeInterface, OtherInterface`
-- `abstract class` (class with incomplete implementation) - not really needed?
-- Class (static) methods and Object methods: syntax to be defined.
-- Class (static) members and Object members: syntax to be defined.
-- Access control: private / protected / shared / public ?  Keep it simple.
-- Access object members with `this.member` ?
-- Generics for class: `class <Tkey, Tentry>`
-- Generics for function: `def <Tkey> GetLast(key: Tkey)`
-- Method overloading (two methods with the same name but different argument
-  types): Most likely not
-- Mixins: not sure if that is useful, leave out for simplicity.
-
-Again, much of this is from TypeScript with a slightly different syntax.
-
-Some things that look like good additions:
-- Use a class as an interface (like Dart)
-- Extend a class with methods, using an import (like Dart)
-- Mixins
-- For testing: Mock mechanism
-
-An important class that will be provided is "Promise".  Since Vim is single
-threaded, connecting asynchronous operations is a natural way of allowing
-plugins to do their work without blocking the user.  It's a uniform way to
-invoke callbacks and handle timeouts and errors.
-
-Some commands have already been reserved:
-       *:class*
-       *:endclass*
-       *:abstract*
-       *:enum*
-       *:endenum*
-       *:interface*
-       *:endinterface*
-       *:static*
-       *:type*
-
-Some examples: >
-
-       abstract class Person
-           static const prefix = 'xxx'
-           var name: string
-
-           def constructor(name: string)
-               this.name = name
-           enddef
-
-           def display(): void
-               echo name
-           enddef
-
-           abstract def find(string): Person
-       endclass
+6. Classes and interfaces                              *vim9-classes*
+
+In legacy script a Dictionary could be used as a kind-of object, by adding
+members that are functions.  However, this is quite inefficient and requires
+the writer to do the work of making sure all the objects have the right
+members.  See |Dictionary-function|.
+
+In |Vim9| script you can have classes, objects and interfaces like in most
+popular object-oriented programming languages.  Since this is a lot of
+functionality it is located in a separate help file: |vim9class.txt|.
+
 
 ==============================================================================
 
@@ -2293,18 +2237,5 @@ tool need to be supported.  Since most languages support classes the lack of
 support for classes in Vim is then a problem.
 
 
-Classes ~
-
-Vim supports a kind-of object oriented programming by adding methods to a
-dictionary.  With some care this can be made to work, but it does not look
-like real classes.  On top of that, it's quite slow, because of the use of
-dictionaries.
-
-It would be good to support real classes, and this is planned for a later
-version.  The support is a "minimal common functionality" of class support in
-most languages.  It will work much like Java, which is the most popular
-programming language.
-
-
 
  vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt
new file mode 100644 (file)
index 0000000..cb86b4d
--- /dev/null
@@ -0,0 +1,697 @@
+*vim9class.txt*        For Vim version 9.0.  Last change: 2022 Dec 04
+
+
+                 VIM REFERENCE MANUAL    by Bram Moolenaar
+
+
+NOTE - This is under development, anything can still change! - NOTE
+
+
+Vim9 classes, objects, interfaces, types and enums.
+
+1.  Overview                   |Vim9-class-overview|
+2.  A simple class             |Vim9-simple-class|
+3.  Using an abstract class    |Vim9-abstract-class|
+4.  Using an interface         |Vim9-using-interface|
+5.  More class details         |Vim9-class|
+6.  Type definition            |Vim9-type|
+7.  Enum                       |Vim9-enum|
+
+9.  Rationale
+10. To be done later
+
+==============================================================================
+
+1. Overview                                    *Vim9-class-overview*
+
+The fancy term is "object-oriented programming".  You can find lots of study
+material about this subject.  Here we document what |Vim9| script provides,
+assuming you know the basics already.  Added are helpful hints about how
+to use this functionality effectively.
+
+The basic item is an object:
+- An object stores state.  It contains one or more variables that can each
+  have a value.
+- An object usually provides functions that manipulate its state.  These
+  functions are invoked "on the object", which is what sets it apart from the
+  traditional separation of data and code that manipulates the data.
+- An object has a well defined interface, with typed member variables and
+  member functions.
+- Objects are created by a class and all objects have the same interface.
+  This never changes, it is not dynamic.
+
+An object can only be created by a class.  A class provides:
+- A new() method, the constructor, which returns an object for the class.
+  This method is invoked on the class name: MyClass.new().
+- State shared by all objects of the class: class variables and constants.
+- A hierarchy of classes, with super-classes and sub-classes, inheritance.
+
+An interface is used to specify properties of an object:
+- An object can declare several interfaces that it implements.
+- Different objects implementing the same interface can be used the same way.
+
+The class hierarchy allows for single inheritance.  Otherwise interfaces are
+to be used where needed.
+
+
+Class modeling ~
+
+You can model classes any way you like.  Keep in mind what you are building,
+don't try to model the real world.  This can be confusing, especially because
+teachers use real-world objects to explain class relations and you might think
+your model should therefore reflect the real world.  It doesn't!  The model
+should match your purpose.
+
+You will soon find that composition is often better than inheritance.  Don't
+waste time trying to find the optimal class model.  Or waste time discussing
+whether a square is a rectangle or that a rectangle is a square.  It doesn't
+matter.
+
+
+==============================================================================
+
+2.  A simple class                             *Vim9-simple-class*
+
+Let's start with a simple example: a class that stores a text position: >
+
+       class TextPosition
+          this.lnum: number
+          this.col: number
+
+          def new(lnum: number, col: number)
+             this.lnum = lnum
+             this.col = col
+          enddef
+
+          def SetLnum(lnum: number)
+             this.lnum = lnum
+          enddef
+
+          def SetCol(col: number)
+             this.col = col
+          enddef
+
+          def SetPosition(lnum: number, col: number)
+             this.lnum = lnum
+             this.col = col
+          enddef
+        endclass
+
+You can create an object from this class with the new() method: >
+
+       var pos = TextPosition.new(1, 1)
+
+The object members "lnum" and "col" can be accessed directly: >
+
+       echo $'The text position is ({pos.lnum}, {pos.col})'
+
+If you have been using other object-oriented languages you will notice that
+in Vim the object members are consistently referred to with the "this."
+prefix.  This is different from languages like Java and TypeScript.  This
+naming convention makes the object members easy to spot.  Also, when a
+variable does not have the "this." prefix you know it is not an object member.
+
+
+Member write access ~
+
+Now try to change an object member directly: >
+
+       pos.lnum = 9
+
+This will give you an error!  That is because by default object members can be
+read but not set.  That's why the class provides a method for it: >
+
+       pos.SetLnum(9)
+
+Allowing to read but not set an object member is the most common and safest
+way.  Most often there is no problem using a value, while setting a value may
+have side effects that need to be taken care of.  In this case, the SetLnum()
+method could check if the line number is valid and either give an error or use
+the closest valid value.
+
+If you don't care about side effects and want to allow the object member to be
+changed at any time, you can make it public: >
+
+       public this.lnum: number
+       public this.col number
+
+Now you don't need the SetLnum(), SetCol() and SetPosition() methods, setting
+"pos.lnum" directly above will no longer give an error.
+
+
+Private members ~
+
+On the other hand, if you do not want the object members to be read directly,
+you can make them private.  This is done by prefixing an underscore to the
+name: >
+
+       this._lnum: number
+       this._col number
+
+Now you need to provide methods to get the value of the private members.
+These are commonly call getters.  We recommend using a name that starts with
+"Get": >
+
+       def GetLnum(): number
+          return this._lnum
+       enddef
+
+       def GetCol() number
+          return this._col
+       enddef
+
+This example isn't very useful, the members might as well have been public.
+It does become useful if you check the value.  For example, restrict the line
+number to the total number of lines: >
+
+       def GetLnum(): number
+          if this._lnum > this._lineCount
+             return this._lineCount
+          endif
+          return this._lnum
+       enddef
+
+
+Simplifying the new() method ~
+
+Many constructors take values for the object members.  Thus you very often see
+this pattern: >
+
+          this.lnum: number
+          this.col: number
+
+          def new(lnum: number, col: number)
+             this.lnum = lnum
+             this.col = col
+          enddef
+
+Not only is this text you need to write, it also has the type of each member
+twice.  Since this is so common a shorter way to write new() is provided: >
+
+          def new(this.lnum, this.col)
+          enddef
+
+The semantics are easy to understand: Providing the object member name,
+including "this.", as the argument to new() means the value provided in the
+new() call is assigned to that object member.  This mechanism is coming from
+the Dart language.
+
+The sequence of constructing a new object is:
+1. Memory is allocated and cleared.  All values are zero/false/empty.
+2. For each declared member that has an initializer, the expression is
+   evaluated and assigned to the member.  This happens in the sequence the
+   members are declared in the class.
+3. Arguments in the new() method in the "this.name" form are assigned.
+4. The body of the new() method is executed.
+
+TODO: for a sub-class the constructor of the parent class will be invoked
+somewhere.
+
+
+==============================================================================
+
+3.  Using an abstract class                    *Vim9-abstract-class*
+
+An abstract class forms the base for at least one sub-class.  In the class
+model one often finds that a few classes have the same properties that can be
+shared, but a class with those properties does not have enough state to create
+an object from.  A sub-class must extend the abstract class and add the
+missing state and/or methods before it can be used to create objects for.
+
+An abstract class does not have a new() method.
+
+For example, a Shape class could store a color and thickness.  You cannot
+create a Shape object, it is missing the information about what kind of shape
+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
+       endclass
+
+       class Square extends Shape
+          this.size: number
+
+          def new(this.size)
+          enddef
+       endclass
+
+       class Triangle extends Shape
+          this.base: number
+          this.height: number
+
+          def new(this.base, this.height)
+          enddef
+       endclass
+<
+                                               *class-member* *:static*
+Class members are declared with "static".  They are used by the name without a
+prefix: >
+
+       class OtherThing
+          this.size: number
+          static totalSize: number
+
+          def new(this.size)
+             totalSize += this.size
+          enddef
+       endclass
+<
+                                               *class-method*
+Class methods are also declared with "static".  They have no access to object
+members, they cannot use the "this" keyword. >
+
+       class OtherThing
+          this.size: number
+          static totalSize: number
+
+          " Clear the total size and return the value it had before. 
+          static def ClearTotalSize(): number
+             var prev = totalSize
+             totalSize = 0
+             return prev
+          enddef
+       endclass
+
+
+==============================================================================
+
+4.  Using an interface                         *Vim9-using-interface*
+
+The example above with Shape, Square and Triangle can be made more useful if
+we add a method to compute the surface of the object.  For that we create the
+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
+       endclass
+
+       interface HasSurface
+          def Surface(): number
+       endinterface
+
+       class Square extends Shape implements HasSurface
+          this.size: number
+
+          def new(this.size)
+          enddef
+
+          def Surface(): number
+             return this.size * this.size
+          enddef
+       endclass
+
+       class Triangle extends Shape implements HasSurface
+          this.base: number
+          this.height: number
+
+          def new(this.base, this.height)
+          enddef
+
+          def Surface(): number
+             return this.base * this.height / 2
+          enddef
+       endclass
+
+The interface name can be used as a type: >
+
+       var shapes: list<HasSurface> = [
+                               Square.new(12),
+                               Triangle.new(8, 15),
+                               ]
+       for shape in shapes
+          echo $'the surface is {shape.Surface()}'
+       endfor
+
+
+==============================================================================
+
+5.  More class details                                 *Vim9-class*
+
+Defining a class ~
+                                       *:class* *:endclass* *:abstract*
+A class is defined between `:class` and `:endclass`.  The whole class is
+defined in one script file.  It is not possible to add to a class later.
+
+It is possible to define more than one class in a script file.  Although it
+usually is better to export only one main class.  It can be useful to define
+types, enums and helper classes though.
+
+The `:abstract` keyword may be prefixed and `:export` may be used.  That gives
+these variants: >
+
+       class ClassName
+       endclass
+
+       export class ClassName
+       endclass
+
+       abstract class ClassName
+       endclass
+
+       export abstract class ClassName
+       endclass
+<
+                                                       *E1314*
+The class name should be CamelCased.  It must start with an uppercase letter.
+That avoids clashing with builtin types.
+
+After the class name these optional items can be used.  Each can appear only
+once.  They can appear in any order, although this order is recommended: >
+       extends ClassName
+       implements InterfaceName, OtherInterface
+       specifies SomeInterface
+<                                                      *extends*
+A class can extend one other class.
+                                                       *implements*
+A class can implement one or more interfaces.
+                                                       *specifies*
+A class can declare it's interface, the object members and methods, with a
+named interface.  This avoids the need for separately specifying the
+interface, which is often done an many languages, especially Java.
+
+
+Defining an interface ~
+                                               *:interface* *:endinterface*
+An interface is defined between `:interface` and `:endinterface`.  It may be
+prefixed with `:export`: >
+
+       interface InterfaceName
+       endinterface
+
+       export interface InterfaceName
+       endinterface
+
+An interface can declare object members, just like in a class but without any
+initializer.
+
+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
+          def Surface(): number
+       endinterface
+
+The "Has" prefix can be used to make it easier to guess this is an interface
+name, with a hint about what it provides.
+
+
+Default constructor ~
+
+In case you define a class without a new() method, one will be automatically
+defined.  This default constructor will have arguments for all the object
+members, in the order they were specified.  Thus if your class looks like: >
+
+       class AutoNew
+          this.name: string
+          this.age: number
+          this.gender: Gender
+       endclass
+
+Then The default constructor will be: >
+
+       def new(this.name, this.age, this.gender)
+       enddef
+
+All object members will be used, also private access ones.
+
+
+Multiple constructors ~
+
+Normally a class has just one new() constructor.  In case you find that the
+constructor is often called with the same arguments you may want to simplify
+your code by putting those arguments into a second constructor method.  For
+example, if you tend to use the color black a lot: >
+
+       def new(this.garment, this.color, this.size)
+       enddef
+       ...
+       var pants = new(Garment.pants, Color.black, "XL")
+       var shirt = new(Garment.shirt, Color.black, "XL")
+       var shoes = new(Garment.shoes, Color.black, "45")
+
+Instead of repeating the color every time you can add a constructor that
+includes it: >
+
+       def newBlack(this.garment, this.size)
+          this.color = Color.black
+       enddef
+       ...
+       var pants = newBlack(Garment.pants, "XL")
+       var shirt = newBlack(Garment.shirt, "XL")
+       var shoes = newBlack(Garment.shoes, "9.5")
+
+Note that the method name must start with "new".  If there is no method called
+"new()" then the default constructor is added, even though there are other
+constructor methods.
+
+
+==============================================================================
+
+6.  Type definition                                    *Vim9-type* *:type*
+
+A type definition is giving a name to a type specification.  For Example: >
+
+       :type ListOfStrings list<string>
+
+TODO: more explanation
+
+
+==============================================================================
+
+7.  Enum                                       *Vim9-enum* *:enum* *:endenum*
+
+An enum is a type that can have one of a list of values.  Example: >
+
+       :enum Color
+               White
+               Red
+               Green
+               Blue
+               Black
+       :endenum
+
+TODO: more explanation
+
+
+==============================================================================
+
+9.  Rationale
+
+Most of the choices for |Vim9| classes come from popular and recently
+developed languages, such as Java, TypeScript and Dart.  The syntax has been
+made to fit with the way Vim script works, such as using `endclass` instead of
+using curly braces around the whole class.
+
+Some common constructs of object-oriented languages were chosen very long ago
+when this kind of programming was still new, and later found to be
+sub-optimal.  By this time those constructs were widely used and changing them
+was not an option.  In Vim we do have the freedom to make different choices,
+since classes are completely new.  We can make the syntax simpler and more
+consistent than what "old" languages use.  Without diverting too much, it
+should still mostly look like what you know from existing languages.
+
+Some recently developed languages add all kinds of fancy features that we
+don't need for Vim.  But some have nice ideas that we do want to use.
+Thus we end up with a base of what is common in popular languages, dropping
+what looks like a bad idea, and adding some nice features that are easy to
+understand.
+
+The main rules we use to make decisions:
+- Keep it simple.
+- No surprises, mostly do what other languages are doing.
+- Avoid mistakes from the past.
+- Avoid the need for the script writer to consult the help to understand how
+  things work, most things should be obvious.
+- Keep it consistent.
+- Aim at an average size plugin, not at a huge project.
+
+
+Using new() for the constructor ~
+
+Many languages use the class name for the constructor method.  A disadvantage
+is that quite often this is a long name.  And when changing the class name all
+constructor methods need to be renamed.  Not a big deal, but still a
+disadvantage.
+
+Other languages, such as TypeScript, use a specific name, such as
+"constructor()".  That seems better.  However, using "new" or "new()" to
+create a new object has no obvious relation with "constructor()".
+
+For |Vim9| script using the same method name for all constructors seemed like
+the right choice, and by calling it new() the relation between the caller and
+the method being called is obvious.
+
+
+No overloading of the constructor ~
+
+In Vim script, both legacy and |Vim9| script, there is no overloading of
+functions.  That means it is not possible to use the same function name with
+different types of arguments.  Therefore there also is only one new()
+constructor.
+
+With |Vim9| script it would be possible to support overloading, since
+arguments are typed.  However, this gets complicated very quickly.  Looking at
+a new() call one has to inspect the types of the arguments to know which of
+several new() methods is actually being called.  And that can require
+inspecting quite a bit of code.  For example, if one of the arguments is the
+return value of a method, you need to find that method to see what type it is
+returning.
+
+Instead, every constructor has to have a different name, starting with "new".
+That way multiple constructors with different arguments are possible, while it
+is very easy to see which constructor is being used.  And the type of
+arguments can be properly checked.
+
+
+No overloading of methods ~
+
+Same reasoning as for the constructor: It is often not obvious what type
+arguments have, which would make it difficult to figure out what method is
+actually being called.  Better just give the methods a different name, then
+type checking will make sure it works as you intended.  This rules out
+polymorphism, which we don't really need anyway.
+
+
+Using "this.member" everywhere ~
+
+The object members in various programming languages can often be accessed in
+different ways, depending on the location.  Sometimes "this." has to be
+prepended to avoid ambiguity.  They are usually declared without "this.".
+That is quite inconsistent and sometimes confusing.
+
+A very common issue is that in the constructor the arguments use the same name
+as the object member.  Then for these members "this." needs to be prefixed in
+the body, while for other members this is not needed and often omitted.  This
+leads to a mix of members with and without "this.", which is inconsistent.
+
+For |Vim9| classes the "this." prefix is always used.  Also for declaring the
+members.  Simple and consistent.  When looking at the code inside a class it's
+also directly clear which variable references are object members and which
+aren't.
+
+
+Single inheritance and interfaces ~
+
+Some languages support multiple inheritance.  Although that can be useful in
+some cases, it makes the rules of how a class works quite complicated.
+Instead, using interfaces to declare what is supported is much simpler.  The
+very popular Java language does it this way, and it should be good enough for
+Vim.  The "keep it simple" rule applies here.  
+
+Explicitly declaring that a class supports an interface makes it easy to see
+what a class is intended for.  It also makes it possible to do proper type
+checking.  When an interface is changed any class that declares to implement
+it will be checked if that change was also changed.  The mechanism to assume a
+class implements an interface just because the methods happen to match is
+brittle and leads to obscure problems, let's not do that.
+
+
+Using class members ~
+
+Using "static member" to declare a class member is very common, nothing new
+here.  In |Vim9| script these can be accessed directly by their name.  Very
+much like how a script-local variable can be used in a function.  Since object
+members are always accessed with "this." prepended, it's also quickly clear
+what kind of member it is.
+
+TypeScript prepends the class name before the class member, also inside the
+class.  This has two problems: The class name can be rather long, taking up
+quite a bit of space, and when the class is renamed all these places need to
+be changed too.
+
+
+Using "ClassName.new()" to construct an object ~
+
+Many languages use the "new" operator to create an object, which is actually
+kind of strange, since the constructor is defined as a method with arguments,
+not a command.  TypeScript also has the "new" keyword, but the method is
+called "constructor()", it is hard to see the relation between the two.
+
+In |Vim9| script the constructor method is called new(), and it is invoked as
+new(), simple and straightforward.  Other languages use "new ClassName()",
+while there is no ClassName() method, it's a method by another name in the
+class called ClassName.  Quite confusing.
+
+
+Default read access to object members ~
+
+Some users will remark that the access rules for object members are
+asymmetric.  Well, that is intentional.  Changing a value is a very different
+action than reading a value.  The read operation has no side effects, it can
+be done any number of times without affecting the object.  Changing the value
+can have many side effects, and even have a ripple effect, affecting other
+objects.
+
+When adding object members one usually doesn't think much about this, just get
+the type right.  And normally the values are set in the new() method.
+Therefore defaulting to read access only "just works" in most cases.  And when
+directly writing you get an error, which makes you wonder if you actually want
+to allow that.  This helps writing code with fewer mistakes.
+
+
+Making object membes private with an underscore ~
+
+When an object member is private, it can only be read and changed inside the
+class (and in sub-classes), then it cannot be used outside of the class.
+Prepending an underscore is a simple way to make that visible.  Various
+programming languages have this as a recommendation.
+
+In case you change your mind and want to make the object member accessible
+outside of the class, you will have to remove the underscore everywhere.
+Since the name only appears in the class (and sub-classes) they will be easy
+to find and change.
+
+The other way around is much harder: you can easily prepend an underscore to
+the object member inside the class to make it private, but any usage elsewhere
+you will have to track down and change.  You may have to make it a "set"
+method call.  This reflects the real world problem that taking away access
+requires work to be done for all places where that access exists.
+
+An alternative would have been using the "private" keyword, just like "public"
+changes the access in the other direction.  Well, that's just to reduce the
+number of keywords.
+
+
+No protected object members ~
+
+Some languages provide several ways to control access to object members.  The
+most known is "protected", and the meaning varies from language to language.
+Others are "shared", "private" and even "friend".
+
+These rules make life more difficult.  That can be justified in projects where
+many people work on the same, complex code where it is easy to make mistakes.
+Especially when refactoring or other changes to the class model.
+
+The Vim scripts are expected to be used in a plugin, with just one person or a
+small team working on it.  Complex rules then only make it more complicated,
+the extra safety provide by the rules isn't really needed.  Let's just keep it
+simple and not specify access details.
+
+
+==============================================================================
+
+10. To be done later
+
+Can a newSomething() constructor invoke another constructor?  If yes, what are
+the restrictions?
+
+Thoughts:
+- Generics for a class: `class <Tkey, Tentry>`
+- Generics for a function: `def <Tkey> GetLast(key: Tkey)`
+- Mixins: not sure if that is useful, leave out for simplicity.
+
+Some things that look like good additions:
+- For testing: Mock mechanism
+
+An important class to be provided is "Promise".  Since Vim is single
+threaded, connecting asynchronous operations is a natural way of allowing
+plugins to do their work without blocking the user.  It's a uniform way to
+invoke callbacks and handle timeouts and errors.
+
+
+ vim:tw=78:ts=8:noet:ft=help:norl:
index a642d91285fe5597a866b0dc99f591448a469bf4..9e9ebe37beb0b47e780d81855fd007c14f7f03fc 100644 (file)
@@ -174,6 +174,7 @@ SRC += \
        userfunc.c \
        version.c \
        viminfo.c \
+       vim9class.c \
        vim9cmds.c \
        vim9compile.c \
        vim9execute.c \
index b559467c19e8d408bf64949042af0fbf1cceefb8..426125663c9a8e4dd530353be2910aa6b138e8a0 100644 (file)
@@ -851,6 +851,7 @@ OBJ = \
        $(OUTDIR)/usercmd.o \
        $(OUTDIR)/userfunc.o \
        $(OUTDIR)/version.o \
+       $(OUTDIR)/vim9class.o \
        $(OUTDIR)/vim9cmds.o \
        $(OUTDIR)/vim9compile.o \
        $(OUTDIR)/vim9execute.o \
@@ -1251,6 +1252,8 @@ $(OUTDIR)/netbeans.o: netbeans.c $(INCL) version.h
 
 $(OUTDIR)/version.o: version.c $(INCL) version.h
 
+$(OUTDIR)/vim9class.o: vim9class.c $(INCL) vim9.h
+
 $(OUTDIR)/vim9cmds.o: vim9cmds.c $(INCL) vim9.h
 
 $(OUTDIR)/vim9compile.o: vim9compile.c $(INCL) vim9.h
index 501446e542bb727c8b66f205e6e5dbd4d8688e63..b478d9aaa807d81ac75e4254a37c8c9c7f3ad7bd 100644 (file)
@@ -735,6 +735,7 @@ OBJ = \
        $(OUTDIR)\undo.obj \
        $(OUTDIR)\usercmd.obj \
        $(OUTDIR)\userfunc.obj \
+       $(OUTDIR)\vim9class.obj \
        $(OUTDIR)\vim9cmds.obj \
        $(OUTDIR)\vim9compile.obj \
        $(OUTDIR)\vim9execute.obj \
@@ -1708,6 +1709,8 @@ $(OUTDIR)/userfunc.obj:   $(OUTDIR) userfunc.c  $(INCL)
 
 $(OUTDIR)/version.obj: $(OUTDIR) version.c  $(INCL) version.h
 
+$(OUTDIR)/vim9class.obj:       $(OUTDIR) vim9class.c  $(INCL) vim9.h
+
 $(OUTDIR)/vim9cmds.obj:        $(OUTDIR) vim9cmds.c  $(INCL) vim9.h
 
 $(OUTDIR)/vim9compile.obj:     $(OUTDIR) vim9compile.c  $(INCL) vim9.h
@@ -1915,6 +1918,7 @@ proto.h: \
        proto/undo.pro \
        proto/usercmd.pro \
        proto/userfunc.pro \
+       proto/vim9class.pro \
        proto/vim9cmds.pro \
        proto/vim9compile.pro \
        proto/vim9execute.pro \
index 53e1ce15a47501a58309c64405f22155e2cb3c87..8dd5454e15e762dcce39763e26896981f718a573 100644 (file)
@@ -409,6 +409,7 @@ SRC = \
        usercmd.c \
        userfunc.c \
        version.c \
+       vim9class.c \
        vim9cmds.c \
        vim9compile.c \
        vim9execute.c \
@@ -534,6 +535,7 @@ OBJ = \
        usercmd.obj \
        userfunc.obj \
        version.obj \
+       vim9class.obj \
        vim9cmds.obj \
        vim9compile.obj \
        vim9execute.obj \
@@ -1122,6 +1124,10 @@ viminfo.obj : viminfo.c vim.h [.auto]config.h feature.h os_unix.h \
  ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
  gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
  errors.h globals.h version.h
+vim9class.obj : vim9class.c vim.h [.auto]config.h feature.h os_unix.h \
+ ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
+ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
+ errors.h globals.h version.h
 vim9cmds.obj : vim9cmds.c vim.h [.auto]config.h feature.h os_unix.h \
  ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
  gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
index 7f9d173a1824541245190a50f5679960e3920328..43249c20aad9d17100b301197e88cbea68717b3d 100644 (file)
@@ -1584,6 +1584,7 @@ BASIC_SRC = \
        usercmd.c \
        userfunc.c \
        version.c \
+       vim9class.c \
        vim9cmds.c \
        vim9compile.c \
        vim9execute.c \
@@ -1741,6 +1742,7 @@ OBJ_COMMON = \
        objects/usercmd.o \
        objects/userfunc.o \
        objects/version.o \
+       objects/vim9class.o \
        objects/vim9cmds.o \
        objects/vim9compile.o \
        objects/vim9execute.o \
@@ -1931,6 +1933,7 @@ PRO_AUTO = \
        usercmd.pro \
        userfunc.pro \
        version.pro \
+       vim9class.pro \
        vim9cmds.pro \
        vim9compile.pro \
        vim9execute.pro \
@@ -3489,6 +3492,9 @@ objects/usercmd.o: usercmd.c
 objects/userfunc.o: userfunc.c
        $(CCC) -o $@ userfunc.c
 
+objects/vim9class.o: vim9class.c
+       $(CCC) -o $@ vim9class.c
+
 objects/vim9cmds.o: vim9cmds.c
        $(CCC) -o $@ vim9cmds.c
 
@@ -4168,6 +4174,11 @@ objects/version.o: version.c vim.h protodef.h auto/config.h feature.h os_unix.h
  proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
  libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
  globals.h errors.h version.h
+objects/vim9class.o: vim9class.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
+ proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
+ libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
+ globals.h errors.h vim9.h
 objects/vim9cmds.o: vim9cmds.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
index 2fc80c5f156f207532f7d3670882149395b31efa..cf30221bcadf31b1d750c87c222b90a94b7ad926 100644 (file)
@@ -3345,3 +3345,7 @@ EXTERN char e_not_allowed_to_change_window_layout_in_this_autocmd[]
        INIT(= N_("E1312: Not allowed to change the window layout in this autocmd"));
 EXTERN char e_not_allowed_to_add_or_remove_entries_str[]
        INIT(= N_("E1313: Not allowed to add or remove entries (%s)"));
+#ifdef FEAT_EVAL
+EXTERN char e_class_name_must_start_with_uppercase_letter_str[]
+       INIT(= N_("E1314: Class name must start with an uppercase letter: %s"));
+#endif
index fd4b1550a20e91e6990544bf3b453fb0868070a0..658f05e2dbda1b2475e0e3dfcf299c498fff717a 100644 (file)
@@ -21,16 +21,16 @@ static const unsigned short cmdidxs1[26] =
   /* n */ 308,
   /* o */ 328,
   /* p */ 340,
-  /* q */ 379,
-  /* r */ 382,
-  /* s */ 402,
-  /* t */ 472,
-  /* u */ 518,
-  /* v */ 529,
-  /* w */ 550,
-  /* x */ 564,
-  /* y */ 574,
-  /* z */ 575
+  /* q */ 380,
+  /* r */ 383,
+  /* s */ 403,
+  /* t */ 473,
+  /* u */ 520,
+  /* v */ 531,
+  /* w */ 552,
+  /* x */ 566,
+  /* y */ 576,
+  /* z */ 577
 };
 
 /*
@@ -56,11 +56,11 @@ static const unsigned char cmdidxs2[26][26] =
   /* m */ {  1,  0,  0,  0,  7,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 16 },
   /* n */ {  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  8, 10,  0,  0,  0,  0,  0, 17,  0,  0,  0,  0,  0 },
   /* o */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  5,  0,  0,  0,  0,  0,  0,  9,  0, 11,  0,  0,  0 },
-  /* p */ {  1,  0,  3,  0,  4,  0,  0,  0,  0,  0,  0,  0,  0,  0,  7,  9,  0,  0, 16, 17, 26,  0, 27,  0, 28,  0 },
+  /* p */ {  1,  0,  3,  0,  4,  0,  0,  0,  0,  0,  0,  0,  0,  0,  7,  9,  0,  0, 16, 17, 26,  0, 28,  0, 29,  0 },
   /* q */ {  2,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* r */ {  0,  0,  0,  0,  0,  0,  0,  0, 12,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 14, 19,  0,  0,  0,  0 },
   /* s */ {  2,  6, 15,  0, 19, 23,  0, 25, 26,  0,  0, 29, 31, 35, 39, 41,  0, 50,  0, 51,  0, 64, 65,  0, 66,  0 },
-  /* t */ {  2,  0, 19,  0, 24, 26,  0, 27,  0, 28,  0, 29, 33, 36, 38, 39,  0, 40, 42,  0, 43,  0,  0,  0, 45,  0 },
+  /* t */ {  2,  0, 19,  0, 24, 26,  0, 27,  0, 29,  0, 30, 34, 37, 39, 40,  0, 41, 43,  0, 44,  0,  0,  0, 46,  0 },
   /* u */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* v */ {  1,  0,  0,  0,  2,  0,  0,  0,  5,  0,  0,  0, 12, 15,  0,  0,  0,  0, 18,  0, 19,  0,  0,  0,  0,  0 },
   /* w */ {  2,  0,  0,  0,  0,  0,  0,  3,  4,  0,  0,  0,  0,  8,  0,  9, 10,  0,  0,  0, 12, 13,  0,  0,  0,  0 },
@@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] =
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };
 
-static const int command_count = 592;
+static const int command_count = 594;
index ed954efc0dff73f8a16740c94e93a96df695bfb2..c6f85ce104758832ee59020848e075b87294de3d 100644 (file)
@@ -126,7 +126,7 @@ EXCMD(CMD_abclear,  "abclear",      ex_abclear,
 EXCMD(CMD_aboveleft,   "aboveleft",    ex_wrongmodifier,
        EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM,
        ADDR_NONE),
-EXCMD(CMD_abstract,    "abstract",     ex_ni,
+EXCMD(CMD_abstract,    "abstract",     ex_class,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
 EXCMD(CMD_all,         "all",          ex_all,
@@ -354,7 +354,7 @@ EXCMD(CMD_clist,    "clist",        qf_list,
 EXCMD(CMD_clast,       "clast",        ex_cc,
        EX_RANGE|EX_COUNT|EX_TRLBAR|EX_BANG,
        ADDR_UNSIGNED),
-EXCMD(CMD_class,       "class",        ex_ni,
+EXCMD(CMD_class,       "class",        ex_class,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT,
        ADDR_NONE),
 EXCMD(CMD_close,       "close",        ex_close,
@@ -567,16 +567,16 @@ EXCMD(CMD_emenu,  "emenu",        ex_emenu,
 EXCMD(CMD_endif,       "endif",        ex_endif,
        EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
        ADDR_NONE),
-EXCMD(CMD_endinterface,        "endinterface", ex_ni,
+EXCMD(CMD_endinterface,        "endinterface", ex_wrongmodifier,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
-EXCMD(CMD_endclass,    "endclass",     ex_ni,
+EXCMD(CMD_endclass,    "endclass",     ex_wrongmodifier,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
 EXCMD(CMD_enddef,      "enddef",       ex_endfunction,
        EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
        ADDR_NONE),
-EXCMD(CMD_endenum,     "endenum",      ex_ni,
+EXCMD(CMD_endenum,     "endenum",      ex_wrongmodifier,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
 EXCMD(CMD_endfunction, "endfunction",  ex_endfunction,
@@ -594,7 +594,7 @@ EXCMD(CMD_endwhile, "endwhile",     ex_endwhile,
 EXCMD(CMD_enew,                "enew",         ex_edit,
        EX_BANG|EX_TRLBAR,
        ADDR_NONE),
-EXCMD(CMD_enum,                "enum",         ex_ni,
+EXCMD(CMD_enum,                "enum",         ex_enum,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT,
        ADDR_NONE),
 EXCMD(CMD_eval,                "eval",         ex_eval,
@@ -756,7 +756,7 @@ EXCMD(CMD_inoremenu,        "inoremenu",    ex_menu,
 EXCMD(CMD_intro,       "intro",        ex_intro,
        EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
-EXCMD(CMD_interface,   "interface",    ex_ni,
+EXCMD(CMD_interface,   "interface",    ex_interface,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
 EXCMD(CMD_isearch,     "isearch",      ex_findpat,
@@ -1215,6 +1215,9 @@ EXCMD(CMD_ptselect,       "ptselect",     ex_ptag,
 EXCMD(CMD_put,         "put",          ex_put,
        EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_REGSTR|EX_TRLBAR|EX_ZEROR|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY,
        ADDR_LINES),
+EXCMD(CMD_public,      "public",       ex_wrongmodifier,
+       EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
+       ADDR_NONE),
 EXCMD(CMD_pwd,         "pwd",          ex_pwd,
        EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
@@ -1488,7 +1491,7 @@ EXCMD(CMD_startgreplace, "startgreplace", ex_startinsert,
 EXCMD(CMD_startreplace,        "startreplace", ex_startinsert,
        EX_BANG|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
-EXCMD(CMD_static,      "static",       ex_ni,
+EXCMD(CMD_static,      "static",       ex_wrongmodifier,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
 EXCMD(CMD_stopinsert,  "stopinsert",   ex_stopinsert,
@@ -1614,6 +1617,9 @@ EXCMD(CMD_tfirst, "tfirst",       ex_tag,
 EXCMD(CMD_throw,       "throw",        ex_throw,
        EX_EXTRA|EX_NEEDARG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
        ADDR_NONE),
+EXCMD(CMD_this,                "this",         ex_wrongmodifier,
+       EX_EXTRA|EX_NEEDARG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
+       ADDR_NONE),
 EXCMD(CMD_tjump,       "tjump",        ex_tag,
        EX_BANG|EX_TRLBAR|EX_WORD1,
        ADDR_NONE),
@@ -1665,7 +1671,7 @@ EXCMD(CMD_tunmenu,        "tunmenu",      ex_menu,
 EXCMD(CMD_tunmap,      "tunmap",       ex_unmap,
        EX_EXTRA|EX_TRLBAR|EX_NOTRLCOM|EX_CTRLV|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
-EXCMD(CMD_type,                "type",         ex_ni,
+EXCMD(CMD_type,                "type",         ex_type,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT,
        ADDR_NONE),
 EXCMD(CMD_undo,                "undo",         ex_undo,
index 218b7edd9b12a36199a4301394a77fcafea9805f..660d32e2a457e3b37d77cba4efb2cba89a96377b 100644 (file)
@@ -267,6 +267,7 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name);
 # define ex_breaklist          ex_ni
 # define ex_call               ex_ni
 # define ex_catch              ex_ni
+# define ex_class              ex_ni
 # define ex_compiler           ex_ni
 # define ex_continue           ex_ni
 # define ex_debug              ex_ni
@@ -282,10 +283,12 @@ static void       ex_tag_cmd(exarg_T *eap, char_u *name);
 # define ex_endif              ex_ni
 # define ex_endtry             ex_ni
 # define ex_endwhile           ex_ni
+# define ex_enum               ex_ni
 # define ex_eval               ex_ni
 # define ex_execute            ex_ni
-# define ex_incdec             ex_ni
 # define ex_finally            ex_ni
+# define ex_incdec             ex_ni
+# define ex_interface          ex_ni
 # define ex_finish             ex_ni
 # define ex_function           ex_ni
 # define ex_if                 ex_ni
@@ -300,6 +303,7 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name);
 # define ex_scriptnames                ex_ni
 # define ex_throw              ex_ni
 # define ex_try                        ex_ni
+# define ex_type               ex_ni
 # define ex_unlet              ex_ni
 # define ex_while              ex_ni
 # define ex_import             ex_ni
@@ -6693,12 +6697,13 @@ ex_recover(exarg_T *eap)
 }
 
 /*
- * Command modifier used in a wrong way.
+ * Command modifier used in a wrong way.  Also for other commands that can't
+ * appear at the toplevel.
  */
     static void
 ex_wrongmodifier(exarg_T *eap)
 {
-    eap->errmsg = _(e_invalid_command);
+    eap->errmsg = ex_errmsg(e_invalid_command_str, eap->cmd);
 }
 
 /*
index 9d1e1349d10972ff84536f744251c4ae27bfadd3..50802ce8e59578e84e8667c00abc17b544545683 100644 (file)
@@ -215,6 +215,7 @@ void mbyte_im_set_active(int active_arg);
 # ifdef FEAT_EVAL
 // include vim9.h here, the types defined there are used by function arguments.
 #  include "vim9.h"
+#  include "vim9class.pro"
 #  include "vim9cmds.pro"
 #  include "vim9compile.pro"
 #  include "vim9execute.pro"
diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro
new file mode 100644 (file)
index 0000000..4e55178
--- /dev/null
@@ -0,0 +1,6 @@
+/* vim9class.c */
+void ex_class(exarg_T *eap);
+void ex_interface(exarg_T *eap);
+void ex_enum(exarg_T *eap);
+void ex_type(exarg_T *eap);
+/* vim: set ft=c : */
index d5a4e935cd1ca8972c53d1c826a0f8abeaf5a75f..323a78e4a0f7adc7736147f9685a1bb2ca1b7a65 100644 (file)
@@ -15,6 +15,16 @@ def Test_cmdmods_array()
   lines = getline(top, bot)
   var mods = lines->map((_, v) => substitute(v, '.*"\(\k*\)".*', '\1', ''))
 
+  # Add the other commands that use ex_wrongmodifier.
+  mods->extend([
+                'endclass',
+                'endenum',
+                'endinterface',
+                'public',
+                'static',
+                'this',
+              ])
+
   # Check the lists are equal.  Convert them to a dict to get a clearer error
   # message.
   var cmds_dict = {}
index 10db2470806acd81add143bd4652e4c21e296da1..68f2470acf2c251d3612b8b1bf47acc373506bb5 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1001,
 /**/
     1000,
 /**/
diff --git a/src/vim9class.c b/src/vim9class.c
new file mode 100644 (file)
index 0000000..b2307cb
--- /dev/null
@@ -0,0 +1,110 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved   by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * vim9class.c: Vim9 script class support
+ */
+
+#define USING_FLOAT_STUFF
+#include "vim.h"
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+
+// When not generating protos this is included in proto.h
+#ifdef PROTO
+# include "vim9.h"
+#endif
+
+/*
+ * Handle ":class" and ":abstract class" up to ":endclass".
+ */
+    void
+ex_class(exarg_T *eap)
+{
+    int is_abstract = eap->cmdidx == CMD_abstract;
+
+    char_u *arg = eap->arg;
+    if (is_abstract)
+    {
+       if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
+       {
+           semsg(_(e_invalid_argument_str), arg);
+           return;
+       }
+       arg = skipwhite(arg + 5);
+    }
+
+    if (!ASCII_ISUPPER(*arg))
+    {
+       semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
+       return;
+    }
+
+    // TODO:
+    // generics: <Tkey, Tentry>
+    //    extends SomeClass
+    //    implements SomeInterface
+    //    specifies SomeInterface
+
+
+    // TODO: handle until "endclass" is found:
+    // object and class members (public, read access, private):
+    //   public this.varname
+    //   public static varname
+    //   this.varname
+    //   static varname
+    //   this._varname
+    //   static _varname
+    //
+    // constructors:
+    //   def new()
+    //   enddef
+    //   def newOther()
+    //   enddef
+    //
+    // methods (object, class, generics):
+    //   def someMethod()
+    //   enddef
+    //   static def someMethod()
+    //   enddef
+    //   def <Tval> someMethod()
+    //   enddef
+    //   static def <Tval> someMethod()
+    //   enddef
+}
+
+/*
+ * Handle ":interface" up to ":endinterface".
+ */
+    void
+ex_interface(exarg_T *eap UNUSED)
+{
+    // TODO
+}
+
+/*
+ * Handle ":enum" up to ":endenum".
+ */
+    void
+ex_enum(exarg_T *eap UNUSED)
+{
+    // TODO
+}
+
+/*
+ * Handle ":type".
+ */
+    void
+ex_type(exarg_T *eap UNUSED)
+{
+    // TODO
+}
+
+
+#endif // FEAT_EVAL