From 0002d4e267eb014dafa666af1ac95be33d950195 Mon Sep 17 00:00:00 2001 From: Lukas Tenbrink Date: Mon, 19 Jan 2026 14:25:47 +0100 Subject: [PATCH] Add GDScript highlighting, enable smart quotes, and fix a warning regarding 'display_version'. --- _extensions/gdscript.py | 1589 +++++++++++++++++++++++++++++++++++++++ conf.py | 8 +- 2 files changed, 1596 insertions(+), 1 deletion(-) create mode 100644 _extensions/gdscript.py diff --git a/_extensions/gdscript.py b/_extensions/gdscript.py new file mode 100644 index 0000000..54b4dc6 --- /dev/null +++ b/_extensions/gdscript.py @@ -0,0 +1,1589 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.gdscript + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for GDScript. + + :copyright: Copyright 2xxx by The Godot Engine Community + :license: MIT. + + modified by Daniel J. Ramirez based on the original python.py pygment + further expanded and consolidated with the godot-docs lexer by Zackery R. Smith and Ste. +""" + +from pygments.lexer import RegexLexer, include, bygroups, words, combined +from pygments.token import ( + Keyword, + Literal, + Name, + Comment, + String, + Number, + Operator, + Whitespace, + Punctuation, +) + +__all__ = ["GDScriptLexer"] + + +class GDScriptLexer(RegexLexer): + """ + For GDScript source code. + """ + + name = "GDScript" + url = "https://www.godotengine.org" + aliases = ["gdscript", "gd"] + filenames = ["*.gd"] + mimetypes = ["text/x-gdscript", "application/x-gdscript"] + + # taken from pygments/gdscript.py + @staticmethod + def inner_string_rules(ttype): + return [ + # the old style '%s' % (...) string formatting + ( + r"%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?" + "[hlL]?[E-GXc-giorsux%]", + String.Interpol, + ), + # backslashes, quotes, and formatting signs must be parsed one at a time + (r'[^\\\'"%\n]+', ttype), + (r'[\'"\\]', ttype), + # unhandled string formatting sign + (r"%", ttype), + # newlines are an error (use "nl" state) + ] + + tokens = { + "whitespace": [(r"\s+", Whitespace)], + "comment": [ + (r"##.*$", Comment.Doc), + (r"#(?:end)?region.*$", Comment.Region), + (r"#.*$", Comment.Single), + ], + "punctuation": [ + (r"[]{}(),:;[]", Punctuation), + (r":\n", Punctuation), + (r"\\", Punctuation), + ], + # NOTE: from github.com/godotengine/godot-docs + "keyword": [ + ( + words( + ( + # modules/gdscript/gdscript.cpp - GDScriptLanguage::get_reserved_words() + # Declarations. + "class", + "class_name", + "const", + "enum", + "extends", + "func", + "namespace", # Reserved for potential future use. + "signal", + "static", + "trait", # Reserved for potential future use. + "var", + # Other keywords. + "await", + "breakpoint", + "self", + "super", + "yield", # Reserved for potential future use. + # Not really keywords, but used in property syntax. + # also colored like functions, not keywords + #"set", + #"get", + ), + suffix=r"\b", + ), + Keyword, + ), + ( + words( + ( + # modules/gdscript/gdscript.cpp - GDScriptLanguage::get_reserved_words() + # Control flow. + "break", + "continue", + "elif", + "else", + "for", + "if", + "match", + "pass", + "return", + "when", + "while", + "yield", + ), + suffix=r"\b", + ), + # Custom control flow class used to give control flow keywords a different color, + # like in the Godot editor. + Keyword.ControlFlow, + ), + ], + "builtin": [ + ( + words( + ("true", "false", "PI", "TAU", "NAN", "INF", "null"), + prefix=r"(?>|&&|\+=|-=|\*=|/=|%=|&=|\|=|\|\||[-~+/*%=<>&^.!|$]", + Operator, + ), + (r"(in|is|and|as|or|not)\b", Operator.Word), + ], + "number": [ + (r"([\d_]+\.[\d_]*|[\d_]*\.[\d_]+)([eE][+-]?[\d_]+)?", Number.Float), + (r"[\d_]+[eE][+-]?[\d_]+", Number.Float), + (r"0[xX][a-fA-F\d_]+", Number.Hex), + (r"(-)?0[bB]([01]|(?<=[01])_)+", Number.Bin), + (r"[\d_]+", Number.Integer), + ], + "name": [(r"[a-zA-Z_]\w*", Name)], + "typehint": [ + (r"[a-zA-Z_]\w*", Name.Class, "#pop"), + ], + "string_escape": [ + ( + r'\\([\\abfnrtv"\']|\n|N\{.*?\}|u[a-fA-F0-9]{4}|' + r"U[a-fA-F0-9]{6}|x[a-fA-F0-9]{2}|[0-7]{1,3})", + String.Escape, + ) + ], + "string_single": inner_string_rules(String.Single), + "string_double": inner_string_rules(String.Double), + "string_other": inner_string_rules(String.Other), + "string_stringname": inner_string_rules(String.StringName), + "string_nodepath": inner_string_rules(String.NodePath), + "double_quotes": [ + (r'"', String.Double, "#pop"), + (r'\\\\|\\"|\\\n', String.Escape), # included here for raw strings + include("string_double"), + ], + "single_quotes": [ + (r"'", String.Single, "#pop"), + (r"\\\\|\\'|\\\n", String.Escape), # included here for raw strings + include("string_single"), + ], + "triple_double_quotes": [ + (r'"""', String.Double, "#pop"), + include("string_double"), + include("whitespace"), + ], + "triple_single_quotes": [ + (r"'''", String.Single, "#pop"), + include("string_single"), + include("whitespace"), + ], + "node_reference": [ + (r'[\$%]"', String.Other, include("node_reference_double")), + (r"[\$%]'", String.Other, include("node_reference_single")), + (r"[\$%][A-Za-z_][\w/]*/?", String.Other), + ], + "node_reference_double": [ + (r'"', String.Other, "#pop"), + include("string_other"), + ], + "node_reference_single": [ + (r"'", String.Other, "#pop"), + include("string_other"), + ], + "stringname": [ + (r'[&]"', String.StringName, include("stringname_double")), + (r"[&]'", String.StringName, include("stringname_single")), + ], + "stringname_double": [ + (r'"', String.StringName, "#pop"), + include("string_stringname"), + ], + "stringname_single": [ + (r"'", String.StringName, "#pop"), + include("string_stringname"), + ], + "nodepath": [ + (r'[\^]"', String.NodePath, include("nodepath_double")), + (r"[\^]'", String.NodePath, include("nodepath_single")), + ], + "nodepath_double": [ + (r'"', String.NodePath, "#pop"), + include("string_nodepath"), + ], + "nodepath_single": [ + (r"'", String.NodePath, "#pop"), + include("string_nodepath"), + ], + "function_name": [(r"[a-zA-Z_]\w*", Name.Function.Declaration, "#pop")], + "enum_name": [(r"[a-zA-Z_]\w*", Name, "#pop")], + "function": [ + (r"\b([a-zA-Z_]\w*)\s*(?=\()", Name.Function), + ( + # colored like functions, even without braces + words(("set", "get",), suffix=r"\b", ), + Name.Function, + ), + ], + + ####################################################################### + # LEXER ENTRY POINT + ####################################################################### + "root": [ + include("whitespace"), + include("comment"), + include("punctuation"), + include("builtin"), + # strings + include("stringname"), + include("nodepath"), + include("node_reference"), + ( + '(r)(""")', + bygroups(String.Affix, String.Double), + "triple_double_quotes", + ), + ( + "(r)(''')", + bygroups(String.Affix, String.Single), + "triple_single_quotes", + ), + ( + '(r)(")', + bygroups(String.Affix, String.Double), + "double_quotes", + ), + ( + "(r)(')", + bygroups(String.Affix, String.Single), + "single_quotes", + ), + ( + '(r?)(""")', + bygroups(String.Affix, String.Double), + combined("string_escape", "triple_double_quotes"), + ), + ( + "(r?)(''')", + bygroups(String.Affix, String.Single), + combined("string_escape", "triple_single_quotes"), + ), + ( + '(r?)(")', + bygroups(String.Affix, String.Double), + combined("string_escape", "double_quotes"), + ), + ( + "(r?)(')", + bygroups(String.Affix, String.Single), + combined("string_escape", "single_quotes"), + ), + # consider Name after a . as instance/members variables + (r"(?