- Joined
- Jul 10, 2009
- Messages
- 534
What this guide is about
This guide will show you how to annotate Lua code, i.e. how to add object types to your function parameters and variables.
Annotations are basically a special type of comment. They don't influence the execution of your code by any means, but just add documentation.
This is especially useful for function parameter types. Lua is not type-safe on its own, so documenting those yourself is a smart option, if you still want to understand today's code by next week.
Good thing, there are standards for Lua annotation, such as Emmy Annotation and it's successor LuaCATS.
The well-known sumneko.lua-extension for Visual Studio Code provides Intellisense features like automatic Syntax Checks and parameter preview based on your annotation.
Hence, this guide will be especially useful for you, if you plan to use the mentioned extension, but even if not, it would still drastically increase documentation quality of your code. Honestly, everybody should use it, including you.
See here, if you'd like to see a code resource that's fully annotated.
The following list summarizes the most useful annotations.
A complete list can be found here.
Please refer to the other tabs for more detailed descriptions and example code.
Function Params
Classes and Types
The following annotations are not restricted to
Advanced Table annotations
Advanced Function annotation
Union-type and Value annotation
Generics
Annotating function parameters is the most important part of the whole annotation thing, because erroneous function calls are the main reason for broken Lua code.
In JASS, parameter types were just part of every function definition and the Syntax Check effectively warned you, when you tried to call it with a wrong-type parameter. Lua syntax check doesn't do that, so we need to rely on our IDE (VSCode + sumneko) instead, which in turn relies on your annotation.
You see, how not producing type-based bugs pretty much boils down to proper annotation!
By default, VSCode + sumneko extension knows about the standard Lua types (
All other types must be added by you, before they can be used in any annotation (see
As mentioned in the overview, A comprehensive guide to Mapping in Lua has definition files for common.lua and blizzard.lua attached, which also include
Tables and functions are complex things on their own, so using the existing types
The advanced methods below show, how these additional information can be integrated into annotations.
They are presented with the
We have cases in coding, where some parameter can have the one type or the other. For instance, assume a function that either takes a
This problem is even more likely to occur in Lua code than it was in Jass, due to the missing type-safety (or should I say due to the existing type-freedom at this point?
).
Plus, we do have functions that don't take any value of a certain type, but you need to pass very specific values for it to work.
Like, consider a function that lets you play Rock, Scissors Paper with the computer - and you need to pass exactly one of the values "Rock", "Scissors" or "Paper".
The annotations below show how to specify that.
Again, the annotation style is shown with the
Generics are a bit special. You basically define a name that denotes any type. You can use that name instead of the existing
A good example is the sorting-function in the example below. It can sort an array of any type, but several parameters are required to have the exact same type.
This guide will show you how to annotate Lua code, i.e. how to add object types to your function parameters and variables.
Annotations are basically a special type of comment. They don't influence the execution of your code by any means, but just add documentation.
This is especially useful for function parameter types. Lua is not type-safe on its own, so documenting those yourself is a smart option, if you still want to understand today's code by next week.
Good thing, there are standards for Lua annotation, such as Emmy Annotation and it's successor LuaCATS.
The well-known sumneko.lua-extension for Visual Studio Code provides Intellisense features like automatic Syntax Checks and parameter preview based on your annotation.
Hence, this guide will be especially useful for you, if you plan to use the mentioned extension, but even if not, it would still drastically increase documentation quality of your code. Honestly, everybody should use it, including you.
See here, if you'd like to see a code resource that's fully annotated.
Overview
Quick Reference
Function params
Types and Classes
Table- and Function-type params
Union-Types and Values
Generics
Motivation
Recommended: Installing VSCode + sumneko extension
A comprehensive guide to Mapping in Lua will show you how to properly setup Visual Studio Code and the sumneko-extension in its chapter IDE Setup. The same thread has definition files attached containing all blizzard natives with Emmy annotation / LuaCATS. Keep those in your VSCode workspace to enable function and parameter previews:
Sneak Peek
Consider the following function, which converts a boolean to a string:
You can see that 3 lines of comments have been added on top of the function, providing a small description as well as input and output parameter types. That's how an annotation looks like.
You could argue that the function looks self-explanatory on its own and doesn't need further annotation, but trust me, this assumption fails every so often. If it does, you spend way more time being confused than annotation would have cost you in the first place.
Using VSCode with the mentioned extension will not only show the Boolean2String example with colorful syntax highlighting, but also provide parameter preview and other intellisense features:
Note that the grey
Below: Parameter preview based on the provided annotation.
It is definitely best practice to annotate every single function in your code, even local ones. It can feel annoying at first, but quickly becomes a satisfying routine that eventually will save a lot of time.
Again and to be clear, writing annotations does not influence execution of your code at all, because it is just a bunch of comments. It rather helps you (and your development environment) to see, what is going on.
Remember: The better your annotation is, the fewer bugs you will cause.
Refer to the other tabs to see all annotations available.
Code-writing in Lua offers great opportunities, but also hat its flaws. Especially the missing type safety can lead to a frustrating experience for the unexperienced user, i.e. the fact that you cannot denote input and output parameter types in function definitions. World Editor Lua Syntax check will not and can not inform you about passing wrong-typed parameters or a wrong number of arguments to a function.
If you do such a mistake, it will just silently break your code. Ingame, when you expect your code to be executed, it might look like "nothing happened".
Even worse, sometimes after we come back to our Lua code after a break, we might not even remember what type a particular parameter in a given function was supposed to have. Maybe we have chosen a poor name for the parameter. Or the parameter needs to be a table with certain attributes, but what was it again? Now we end up reading through the function code again to re-catch that missing information.
The solution to this problem is, surprise, proper code annotation. Once annotated, IDE extensions like sumneko.lua will check your syntax for you and warn you, when you do something wrong.
If you do such a mistake, it will just silently break your code. Ingame, when you expect your code to be executed, it might look like "nothing happened".
Even worse, sometimes after we come back to our Lua code after a break, we might not even remember what type a particular parameter in a given function was supposed to have. Maybe we have chosen a poor name for the parameter. Or the parameter needs to be a table with certain attributes, but what was it again? Now we end up reading through the function code again to re-catch that missing information.
The solution to this problem is, surprise, proper code annotation. Once annotated, IDE extensions like sumneko.lua will check your syntax for you and warn you, when you do something wrong.
Recommended: Installing VSCode + sumneko extension
A comprehensive guide to Mapping in Lua will show you how to properly setup Visual Studio Code and the sumneko-extension in its chapter IDE Setup. The same thread has definition files attached containing all blizzard natives with Emmy annotation / LuaCATS. Keep those in your VSCode workspace to enable function and parameter previews:
Sneak Peek
Consider the following function, which converts a boolean to a string:
Lua:
---Boolean2String
---@param b boolean
---@return string
function B2S(b)
if b then
return "TRUE"
end
return "FALSE"
end
You could argue that the function looks self-explanatory on its own and doesn't need further annotation, but trust me, this assumption fails every so often. If it does, you spend way more time being confused than annotation would have cost you in the first place.
Using VSCode with the mentioned extension will not only show the Boolean2String example with colorful syntax highlighting, but also provide parameter preview and other intellisense features:
Note that the grey
:boolean
-box in the function-brackets is not part of the code, but rather displayed by the sumneko-extension.Below: Parameter preview based on the provided annotation.
It is definitely best practice to annotate every single function in your code, even local ones. It can feel annoying at first, but quickly becomes a satisfying routine that eventually will save a lot of time.
Again and to be clear, writing annotations does not influence execution of your code at all, because it is just a bunch of comments. It rather helps you (and your development environment) to see, what is going on.
Remember: The better your annotation is, the fewer bugs you will cause.
Refer to the other tabs to see all annotations available.
The following list summarizes the most useful annotations.
A complete list can be found here.
Please refer to the other tabs for more detailed descriptions and example code.
Function Params
Classes and Types
The following annotations are not restricted to
---@type
, but also work with ---@param
, ---@return
and ---@field
.Advanced Table annotations
Advanced Function annotation
Union-type and Value annotation
Generics
Annotating function parameters is the most important part of the whole annotation thing, because erroneous function calls are the main reason for broken Lua code.
In JASS, parameter types were just part of every function definition and the Syntax Check effectively warned you, when you tried to call it with a wrong-type parameter. Lua syntax check doesn't do that, so we need to rely on our IDE (VSCode + sumneko) instead, which in turn relies on your annotation.
You see, how not producing type-based bugs pretty much boils down to proper annotation!
Annotation | |
Example: | |
Further Notes: | Just skip the ---@return -annotation, if the function doesn't have return values.You can specify multiple return values with multiple ---@return -statements (or one statement with comma-separated types).Older versions of the sumneko-extension (2.X) didn't support ---@param ... type annotation. Instead, you had to use ---@vararg type .If any parameter is a function itself, you might want to annotate that function's parameters as well instead of just writing ---@param name function . The same is true for tables with named entries. For that, please refer to the section for advanced typing. |
By default, VSCode + sumneko extension knows about the standard Lua types (
string
, number
, integer
, float
, boolean
, function
, table
, userdata
, thread
, nil
and the generic any
).All other types must be added by you, before they can be used in any annotation (see
---@class
-statement below). That's also true for Warcraft native types like unit
, location
, etc.As mentioned in the overview, A comprehensive guide to Mapping in Lua has definition files for common.lua and blizzard.lua attached, which also include
---@class
-statements for all Warcraft types. So better use those instead of declaring them yourself Annotation | |
Example: | To comply with standard examples from university, let us assume we want to create Car -objects in your code. Cars are a special type of Vehicle and each Vehicle has a VehicleBrand among other attributes.These object types can be annotated in way that is shown below. Note how each declared class can immediately be used as a type in other annotations, like ---@type .The same example denoted in a different flavor: The second variant will have the same effect on the Intellisense features, but might have additional advantages for object oriented programming, if you plan to use the __index -metamethod. |
Further Notes: | The ---@type is only necessary to use, when the variable type is not clear by context. For example, The ---@field -annotation allows to specify the scope of the attribute (private , public , protected and package ). This influences, where in your code the field will show up in attribute previews.Example: Not specifying a scope is the same as public scope. The examples above are using the ---@type -annotation on the right of the table entries, but that's not the only way.Using the line directly above is also fine and many people prefer to do so:
Lua:
|
Tables and functions are complex things on their own, so using the existing types
function
and table
might not be enough to suit your needs. In fact, written code often depends on that the table passed to a certain function has certain attributes or that the passed function takes and returns a certain type.The advanced methods below show, how these additional information can be integrated into annotations.
They are presented with the
---@type
-annotation, but the same logic applies for ---@param
, ---@vararg
, ---@return
and ---@field
.Annotations for tables | |
Annotations for functions: | |
Example: |
We have cases in coding, where some parameter can have the one type or the other. For instance, assume a function that either takes a
unit
or a destructable
. You can use the widget
-type, but then how do you specify that items are not allowed?This problem is even more likely to occur in Lua code than it was in Jass, due to the missing type-safety (or should I say due to the existing type-freedom at this point?
Plus, we do have functions that don't take any value of a certain type, but you need to pass very specific values for it to work.
Like, consider a function that lets you play Rock, Scissors Paper with the computer - and you need to pass exactly one of the values "Rock", "Scissors" or "Paper".
The annotations below show how to specify that.
Again, the annotation style is shown with the
---@type
-annotation, but the same logic applies for ---@param
, ---@vararg
, ---@return
and ---@field
.Annotation | |
Example: | |
Further Notes: | In older versions of the sumneko extension (2.X), values always had to be engulfed by single quotation marks. That means the number 0 had to be annotated as '0' and the string "Hello World" as '"Hello World"' .As shown in the example above, values and types can be mixed in the same union. Specifying values will let VSCode prompt you the available options in a drop-down upon using the function, which is very useful to prevent typos: |
Generics are a bit special. You basically define a name that denotes any type. You can use that name instead of the existing
any
-type for other annotations in the same context to show that they all require the same type.A good example is the sorting-function in the example below. It can sort an array of any type, but several parameters are required to have the exact same type.
Annotation | |
Example: |
Further Notes: | As of sumneko-extension 3.X, you can also use generics in a class-context rather than just within functions: This feature is not yet fully developed by the sumneko-extension, though. You will definitely encounter bugs here and there, such as missing method-preview for objects tagged with the type as above. Let's hope that sumneko continues to stabilize over time. Generic classes were unavailable in extension versions 2.X. |
Last edited: