YoYoGames / GameMaker-Bugs

Public tracking for GameMaker bugs
26 stars 8 forks source link

TextMate grammar for Linguist #3017

Open TheWitheredStriker opened 1 year ago

TheWitheredStriker commented 1 year ago

Is your feature request related to a problem?

Linguist is the provider of GitHub's syntax highlighting. It currently does not have an actual grammar for GML, so they use a C++ highlighter for GML code instead. As you can likely tell, this is not a great reading experience: image

Describe the solution you'd like

It'd be great if someone could submit a GML grammar file (e.g. a TextMate one) to Linguist per the steps here and here.

Describe alternatives you've considered

I originally opened this issue on the repository for Scotch, a GML VS Code extension. Just now, the maintainer informed me that GameMaker is moving more towards open source along with plans to support TextMate grammar, and thus told me to open a ticket here, so that I could have it handled in an official capacity.

I also attempted, long ago, to add a GML grammar to Linguist myself (but couldn't get the commands to work).

Additional context

NovaLightshow allows grammars to be tested on code in the browser.

adam-coster commented 1 year ago

I'm assuming that YoYo has a TextMate grammar already for GML, but if not here's a draft I wrote that does a decent job without a lot of complexity:

TextMate Grammar for GML (JSON) ```json { "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", "name": "GameMaker Language", "scopeName": "source.gml", "fileTypes": [ "gml" ], "patterns": [ { "include": "#jsdoc" }, { "include": "#comments" }, { "include": "#regions" }, { "include": "#keywords" }, { "include": "#constants" }, { "include": "#operators" }, { "include": "#functions" }, { "include": "#function_calls" }, { "include": "#numbers" }, { "include": "#macros" }, { "include": "#structs" }, { "include": "#variables" }, { "include": "#strings" } ], "repository": { "comments": { "patterns": [ { "name": "comment.line.triple-slash.gml", "match": "(///).*$\\n?" }, { "name": "comment.line.double-slash.gml", "match": "(//).*$\\n?" }, { "name": "comment.block.gml", "begin": "/\\*", "end": "\\*/", "beginCaptures": { "0": { "name": "punctuation.definition.comment.begin.gml" } }, "endCaptures": { "0": { "name": "punctuation.definition.comment.end.gml" } } } ] }, "regions": { "patterns": [ { "name": "comment.line.region.start.gml", "match": "(#region\\b)(\\s+.*)?$", "captures": { "1": { "name": "keyword.region.begin.gml" }, "2": { "name": "comment.line.region.title.gml" } } }, { "name": "keyword.region.end.gml", "match": "#endregion\\b" } ] }, "keywords": { "patterns": [ { "name": "storage.type.gml", "match": "\\b(var|globalvar|enum)\\b" }, { "name": "storage.modifier.gml", "match": "\\b(static)\\b" }, { "name": "storage.type.class", "match": "\\b(constructor)\\b" }, { "name": "keyword.operator.new.gml", "match": "\\b(new)\\b" }, { "name": "keyword.control.gml", "match": "\\b(begin|end|if|then|else|while|do|for|break|continue|with|until|repeat|exit|return|switch|case|default|global|try|catch|finally|throw|delete)\\b" } ] }, "operators": { "patterns": [ { "name": "keyword.operator.arithmetic.gml", "match": "([-+*/%]|\\b(mod|div)\\b)" }, { "name": "keyword.operator.comparison.gml", "match": "(?:<|>|<=|>=|==|!=)" }, { "name": "keyword.operator.logical.gml", "match": "(?:&&|\\|\\||\\^\\^|!|\\b(?:and|or|xor|not)\\b)" }, { "name": "keyword.operator.assignment.gml", "match": "(?:\\+=|-=|\\*=|/=|%=|\\|=|&=|\\^=|\\?\\?=|<<=|>>=|>>>=|=)" }, { "name": "keyword.operator.bitwise.gml", "match": "(?:&|\\||\\^|<<|>>|>>>|~)" }, { "name": "keyword.operator.ternary.gml", "match": "\\?|:" } ] }, "constants": { "patterns": [ { "name": "constant.language.gml", "match": "\\b(true|false|pi|NaN|infinity|self|other|noone|all|global|undefined|pointer_invalid|pointer_null)\\b" } ] }, "functions": { "patterns": [ { "name": "storage.type.function.gml", "match": "\\b(function)\\b" }, { "name": "entity.name.function.gml", "match": "\\bfunction\\s+(\\w+)\\s*\\(" } ] }, "function_calls": { "patterns": [ { "name": "meta.function-call.gml", "match": "\\b(\\w+)\\s*\\(", "captures": { "1": { "name": "support.function.gml" } } } ] }, "strings": { "patterns": [ { "name": "string.quoted.double.gml", "begin": "[@$]?\"", "end": "\"", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.gml" } }, "endCaptures": { "0": { "name": "punctuation.definition.string.end.gml" } }, "patterns": [ { "include": "#escape_sequences" } ] } ] }, "numbers": { "patterns": [ { "name": "constant.numeric.integer.hexadecimal.gml", "match": "\\b(0x[0-9a-fA-F]+|\\$[0-9a-fA-F]+)\\b" }, { "name": "constant.numeric.integer.binary.gml", "match": "\\b0b[01]+\\b" }, { "name": "constant.numeric.integer.decimal.gml", "match": "\\b\\d+\\b" } ] }, "macros": { "patterns": [ { "name": "storage.type.macro.gml", "begin": "(#macro)\\s+(\\w+)\\s*", "end": "(?
ScottGrogin commented 9 months ago

I found this GML.tmLanguage file inside of the GameMaker.app contents /Applications/GameMaker.app/contents/MacOS/x86_64/TextEditor2/Languages/GML.tmLanguage

GML.tmLanguage ```xml fileTypes gml firstLineMatch name GML patterns match \b(return|if|else|for|while|repeat|do|until|with|continue|break|switch|case|default|exit|then|new|delete|try|catch|finally|throw|static|enum|constructor)\b name keyword.control.gml match \b(div|mod|and|or|xor|not)\b name keyword.operator.gml match \b(undefined|true|false|global|self|other)\b name support.constant.gml match \b(alarm|id|x|y|async_load|argument\d*)\b name support.variable.gml match (?x) ( (?i: \$ ([0-9A-Fa-f_]+)? # Hexadecimal | \b 0x ([0-9A-Fa-f_]+)? # Hexadecimal | \b 0b ([0-1_]+)? # Binary | \b ([0-9]+[0-9_]*) # Decimal | (\#(([A-Fa-f0-9]{2}){1,3})\b) ) | \b([0-9]+[0-9_]*)?(\.\B|\.([0-9]+)\b) ) name constant.numeric.gml begin " beginCaptures 1 name punctuation.definition.string.begin.gml end " endCaptures 1 name punctuation.definition.string.end.gml name string.quoted.double.gml patterns include #string_escaped_char include #string_placeholder begin @" beginCaptures 0 name punctuation.definition.string.begin.gml end " endCaptures 0 name punctuation.definition.string.end.gml name string.quoted.unescaped.double.gml patterns include #string_placeholder match (?x) (globalvar)\s+ ([a-zA-Z_][a-zA-Z0-9_]*){0,1} captures 1 name keyword.control.gml 2 name variable.other.local.gml storage.modifier.global.gml match (?x) (var)\s+ ([a-zA-Z_][a-zA-Z0-9_]*){0,1} captures 1 name keyword.control.gml 2 name variable.other.local.gml 3 name variable.other.local.gml match (?x) (function)\s+ ([a-zA-Z_][a-zA-Z0-9_]*){0,1} captures 1 name keyword.control.gml 2 name entity.name.function.gml begin (?x) ^\s*(\#macro|\#define)\s+ ([a-zA-Z_][a-zA-Z0-9_]*){0,1} beginCaptures 1 name keyword.control.gml 2 name entity.name.function.preprocessor.gml end (?=(?://|/\*))|$ name meta.preprocessor.macro.gml patterns match (?>\\\s*\n) name punctuation.separator.continuation.gml include $base include #block include #parens include #brackets patterns begin /\* beginCaptures 0 name punctuation.definition.comment.begin.gml end \*/ endCaptures 0 name punctuation.definition.comment.end.gml name comment.block.gml patterns include #jsdoc_directive begin // end \n name comment.line.double-slash.gml patterns include #jsdoc_directive repository block patterns begin (\{|begin) beginCaptures 0 name punctuation.section.block.begin.gml end (\}|end) endCaptures 0 name punctuation.section.block.end.gml name meta.block.gml patterns include #block_contents block_contents patterns include #block include $base parens begin \( beginCaptures 0 name punctuation.section.parens.begin.gml end \) endCaptures 0 name punctuation.section.parens.end.gml name meta.parens.gml patterns include $base brackets begin \[(\||\?|\#|\@|\$){0,1} beginCaptures 0 name punctuation.section.bracket.begin.gml end \] endCaptures 0 name punctuation.section.bracket.end.gml name meta.bracket.gml patterns include $base string_escaped_char patterns match \\(\\|[nrabftv"\n]|[0-7]+|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{1,6}) name constant.character.escape.gml match \\. name invalid.illegal.character.escape.gml string_placeholder match \{\d+\} name constant.other.placeholder.gml jsdoc_directive patterns match \@(?:description|desc|pure|ignore|deprecated|context|self|url)\b name storage.type.class.jsdoc.gml match (\@(?:parameter|param|arg|argument))\s+(\{*[A-Za-z0-9_\>\<\.]*\}*){0,1}\s+(\w+)* captures 1 name storage.type.class.jsdoc.gml 2 name storage.type.type.jsdoc.gml 3 name storage.type.name.jsdoc.gml match (\@(?:returns|return))\s+(\{*[A-Za-z0-9_\>\<\.]*\}*){0,1} captures 1 name storage.type.class.jsdoc.gml 2 name storage.type.type.jsdoc.gml match (?:(TODO|HACK)\b).* name comment.callout.gml match https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*) name comment.url.gml scopeName source.gml uuid 0db5e8a9-5929-4d19-af5e-be460d479efa ```
rwkay commented 9 months ago

Congratulations you are uncovering all our secrets... we use TextMate grammars for the New Code Editor - so you will find this getting updated as we move forward

adam-coster commented 9 months ago

Oooo, nice! @rwkay what's the licensing on this? Can I modify and distribute this official tmlanguage file in my projects?

rwkay commented 9 months ago

I would wait until New Code Editor has been released and we have most of the issues sorted (just now it is only internal testing from the QA team, until it has been hammered by the community for a few months, I would only consider it Beta).