This is a "cheat sheet" for "gml_string" extension by YellowAfterlife.
The extension can be acquired from or GM:Marketplace

Click on sections to expand/collapse them.
Quick display controls: Categories · Sections · Everything ·



gml_string_parse(codeString, outList)int

Parses an input string of GML code and pushes the resulting tokens into a ds_list (outList).

The list will not be cleared prior, so you should ds_list_clear it yourself unless you are trying to concatenate results from several parse calls.

Returns the number of tokens added to the list.



This global variable stores the number of lines found during the last call to gml_string_parse. Note that it does not count for any pre-existing tokens that might have been in your list.



This function does a post-process pass on a token list to do a few things:

  • Highlights local variables (var)
  • Highlights global variables (globalvar)
  • Highlights macros (#macro)
  • Highlights enums (enum <name> { ...constructs })
  • Highlights locally defined scripts (#define)

Processing time is usually similar to that of initial parse operation.

tokens = ds_list_create();
gml_string_parse("var i = 1, k = 2; return i + k;", tokens);
gml_string_draw(sx, sy, tokens, colors, ?fonts)

Draws a list of tokens onto screen.

colors is an array of colors per token kind.

fonts (optional) is an array of fonts per token kind.

tokens = ds_list_create();
gml_string_parse("return 1;", tokens);
colors = array_create(gml_string_kind_sizeof, $C0C0C0);
colors[gml_string_kind_keyword] = $71BBFF;

// ...later in Draw event
gml_string_draw(10, 10, tokens, colors);
gml_string_draw_culled(sx, sy, tokens, ytop, ybot, colors, ?fonts)

Like gml_string_draw, but skips drawing tokens outside the ytop..ybot range.

This is handy if you are drawing snippets substantially larger than the viewport, as it'll offer better performance.


The following allow you to customize how parser will deal GML API.


Target GML version.
Can be set to 1 for GMS1 or 2 for GMS2.
This affects how strings will be parsed.


If you do not want tab characters in tokenized output (e.g. GameMaker cannot draw them via draw_text), you can set this variable to a sequence of spaces to use instead.

gml_string_api_tab_string = "    "; // (4 spaces - default)
gml_string_api_tab_string = undefined; // (keep tab characters)
gml_string_api_ident_map:map<name:string, token_kind>

A map with key-value pairs for matching names to token types.

By default this is pre-populated with GML keywords, but you may set additional key-value pairs in it to also highligh your desired functions/constants/variables/etc.

gml_string_api_ident_map[?"abs"] = gml_string_kind_function;
gml_string_api_ident_map[?"fps"] = gml_string_kind_variable;
gml_string_api_ident_map[?"c_white"] = gml_string_kind_constant;
// ...and so on
gml_string_api_enum_map:map<enum_name, map<field_name, any>>

Maps can be assigned into this map to have gml_string know about global enums during hinting pass.

Values inside inner maps do not matter - gml_string will only run ds_map_exists on them.

Note that you should still assign entries into gml_string_api_ident_map.

gml_string_api_ident_map[?"MyEnum"] = gml_string_kind_enum;
var e = ds_map_create();
e[?"A"] = true;
e[?"B"] = true;
gml_string_api_enum_map[?"MyEnum"] = e;
gml_string_api_flow_keyword_map:map<name:string, is_flow:bool>

This ds_map specifies keywords that immediately end processing of local variable declarations (var). You shouldn't have to touch it unless you are using custom keywords and also do not require semicolons.


Adds entries to gml_string_api_ident_map as per definitions in GameMaker fnames-style format. This can be more convenient than assigning identifiers to gml_string_api_ident_map.

The following will be recognized:

  • variable_name
  • constant_name#
  • function_name(

For example,


would load API entries from a "custom.txt" file.


Like above, but takes fnames content from a string instead.

For example,


If you need to display reasonably big (>1000 lines of code) snippets, you may find that there is a minor cost to drawing tokens closer to the end of the token list simply because the drawing function has to loop over them first to find the first index within the viewport bounds.

These two functions aid with such use cases.

gml_string_nest(tokens, ?lineCount)array<array<token>>

Converts a list of tokens into an array of lines, where each line is an array of tokens residing on that line.

If the optional lineCount argument is supplied, it will be used instead of iterating over the list to count the lines before allocating an array.
You can retrieve it from Note that supplying an incorrect lineCount may result in either operation taking longer than it should (due to re-allocating the array on out-of-bounds write) or gml_string_draw_nested crashing due to a missing array in an index.

gml_string_draw_nested(sx, sy, bakedArray, ytop, ybot, colors, ?fonts)

Draws a 2d array of tokens that you've got from gml_string_nest.
Apart of performance improvements, acts identically to gml_string_draw_culled.


Each token is an array where the first item is token kind and the second item is token text. Trailing items are allowed and will be ignored by the extension's functions.

Kind can be any index so long as your provided color/font arrays include it.


Not included in the extension's normal output.
This can be used when manually adding tokens (e.g. to have a non-highlighted prefix/suffix).


Syntactically invalid characters
(e.g. if you were to have var pound_sign = £;, £ would be error-kind)


Spaces and tabs. Adjacent characters are joined together into single tokens.


"new line" token. These are not present after nesting tokens.


A single-line comment (// text)


A block comment (/* text */)


#define very specifically


Script name following a #define


Keywords, including operator-keywords


Curly brackets (as it is common to style them differently)


Actual operators and assisting tokens (;, :, (, )...)


Identifiers not recognized as keywords or other structures


Variables marked via globalvar


Enum names (E for E.A)


Numeric literals (1, 1.2, $12, 0x12)


Number of "built-in" token types.
Custom token types should count up from this.

Helper functions:


Returns whether a token is a "space" kind of token (spaces, new lines, comments).


Returns a display name for the given token kind, e.g. "curly_bracket" for gml_string_kind_curly_bracket.

JavaScript API

The JS version of the extension exposes a global gml_string object with the following fields mirroring the GML API:

The following are unique to JS API:

renderToHTML(tokens, cssClassPrefix="")string

Example renderer function.

Takes a list of tokens and a CSS class prefix so

var tks = [];
gml_string.parse("return 1", tks);
console.log(gml_string.renderToHTML(tks, "gml"));

would give you

<span class="gmlKeyword">return</span> <span class="gmlNumber">1</span>

Notes on types:

  • ds_list is replaced with array.
  • ds_map is replaced with a prototype-less (Object.create(null)) JS object.
  • Tokens are replaced by mini-objects with kind and text fields.
Haxe API

Haxe API is represented by the gs.* package.

It is what is used to generate GML and JS versions.

Type overview:

  • type.*: Various externs for mapping types optimal for the platform.
  • GsAPI: Maps out API functions.
Default vs nested

As per nesting section, you will generally want to convert your token list to nested format if the line count is too high.

Nested mode in GMS<=2.2

Due to very large 1d arrays not actually being 1d in pre-2.3 versions of GMS, you can only have up to 32000 lines in a nested structure and up to 32000 tokens per such line.

Needless to say, this is far more than realistic use cases need, but if you do need more, you would want to look at the implementation and make a custom version that either writes lengths as prefixes (since arr[32003] accesses arr[1,3]), or structure it to add/remove lists to a list.

Note on use in code editors

Although it may be very tempting to utilize this extension to create a code editor in GameMaker, you should be aware that there's much more to it than syntax highlighting;

Aside of actual editing, to allow editing particularly large files, you would eventually want to not re-highlight an entire file upon typing.