-
Notifications
You must be signed in to change notification settings - Fork 48
Using @hint comments
GameMaker Studio 2.3 brings exciting changes including structures (lightweight, anonymous objects) and constructors.
This might make it tempting to structure your code in object-oriented way... until you realize that GML being a dynamic language means that you don't get contextual auto-completion when doing Something.¦
or myVar.¦
. But, not to worry, GMEdit can help you with this.
This feature further builds upon local variable types along with its own syntax.
In and out of 2.3, @hint
can also be used to mark fields of globalvar
s or macros.
If you tell GMEdit which variables/functions your type has, GMEdit will use that information to show you just that;
For general uses, you will not need to do much at all.
For constructors with standard-issue static variable declarations and assignments, you only have to subsequently specify the type for your local variables to enjoy the features.
So, if you had
function Vec2(_x, _y) constructor {
x = _x;
y = _y;
static add = function(v) {
return new Vec2(x + v.x, y + v.y);
}
static toString = function() {
return "Vec2(" + string(x) + ", " + string(y) + ")";
}
}
and then wrote var v:Vec2;
, saved (for GMEdit to take note of added type), and typed v.
, you would get an auto-completion list with only your 2 variables and 2 functions in it, along with correct arguments for those functions.
Comment syntax is as following:
/// @hint [{type}] [new] [TypeName][.:][field][(...arguments)[->returnType]] [...description]
1 2 3 4 5 6 7 8 9
- Comment start and
@hint
meta. Spaces can vary. - Optional type for fields.
(note: when using with methods, this is the type of function itself, not the return type) -
new
keyword if what you are declaring is a constructor. - Type name.
Can be omitted if your hints are inside a top-level function/constructor. -
.
to declare a static field (accessed asType.field
) or:
to declare an instance field (accessed asvar v:Type
and thenv.field
). - Field/function name.
Can be omitted (along with.
) if you want to specify constructor/self-call arguments (more on this later) - If the field is a function, you may also specify arguments.
- If the field is a function, you may specify a return type.
This should contain no spaces;->
arrow will be converted to➜
. - Optional short description, which will be shown in auto-completion popups on a second line.
A few special versions are also available:
/// @hint TypeName extends ParentTypeName
Marks ParentTypeName
as parent of TypeName
, inheriting any non-static variables (much like object/struct inheritance would)
/// @hint TypeName implements InterfaceName
Adds InterfaceName
to list of TypeName
's interfaces, much like @implements would.
Suppose you have the aforementioned Vector2 constructor and decide that it'd be really nice to have some static helper functions.
You cannot assign variables to a top-level function (as it is compiled into a raw ID), but you could create a global variable and assign a constructor into it (which would now be a method-struct), subsequently also assigning your static methods:
globalvar Vector2;
Vector2 = function(_x, _y) constructor {
x = _x;
y = _y;
static add = function(v) {
return new Vec2(x + v.x, y + v.y);
};
static toString = function() {
return "Vec2(" + string(x) + ", " + string(y) + ")";
};
};
Vector2.Lerp = function(a, b, t) {
return new Vector2(lerp(a.x, b.x, t), lerp(a.y, b.y, t));
}
Reasonably enough, this is the exact point where the normal auto-completion would poop the pants - there's no way of knowing for sure what you'll assign into Vector2
variable, even with complicated code analysis.
With @hint
, you could then add the following after your declarations:
/// @hint new Vector2(x, y)
/// @hint Vector2:add(v)->Vector2
/// @hint Vector2:toString()->
/// @hint Vector2.Lerp(v1, v2, factor)->Vector2
which would suggest you the Lerp function when typing Vector2.
and your instance functions when doing var v:Vector2
.. and then typing v.
.
It is possible to use @hint
to annotate callable types, like so:
function Array() {
var _arr = array_create(argument_count);
for (var i = 0; i < argument_count; i++) _arr[i] = argument[i];
//
var this = method({ arr: _arr }, function() {
if (argument_count > 1) {
arr[@argument[0]] = argument[1];
} else return arr[argument[0]];
});
with (this) {
arr = _arr;
function resize(newsize) {
array_resize(arr, newsize);
}
function toString() {
return string(arr);
}
}
return this;
}
/// @hint Array(...values)
/// @hint Array:(index, ?newValue)
/// @hint Array:resize(newsize)
/// @hint Array:toString()->string
function scr_hello() {
var a = array("a", "b", "c");
trace(a(1)); // getter, returns "b"
a(1, "hi!"); // setter, changes item 1
a.resize(4); //
trace(a); // [ "a","hi!","c",0 ]
}
This is obviously fairly exotic, but can be handy for an occasion.
You can use templates both after types and after fields, e.g.
/// @hint new ArrayList<T>()
/// @hint ArrayList<T>:push(...values:T)
/// @hint ArrayList.create<T>(size:int, ?value:T)->ArrayList<T>
- When using
/// @hint Type(...)
or/// @hint new Type(...)
specifically, hint-comment should be after theglobalvar
or#macro
declaration of the base field.
- Smart auto-completion
- Types
- JSDoc tags (incl. additional ones)
- @hint tag (mostly 2.3)
- `vals: $v1 $v2` (template strings)
- #args (pre-2.3 named arguments)
- ??= (for pre-GM2022 optional arguments)
- ?? ?. ?[ (pre-GM2022 null-conditional operators)
- #lambda (pre-2.3 function literals)
- => (2.3+ function shorthands)
- #import (namespaces and aliases)
- v:Type (local variable types)
- #mfunc (macros with arguments)
- #gmcr (coroutines)