From e99e16190e5f9023c29786e2452dc8b37881772c Mon Sep 17 00:00:00 2001 From: Owen Voke Date: Mon, 11 May 2026 14:59:38 +0100 Subject: [PATCH] feat: add support for GraphQL language --- src/Highlighter.php | 2 + src/Languages/Graphql/GraphqlLanguage.php | 73 +++++++++++++++++ .../Patterns/GraphqlCommentPattern.php | 35 ++++++++ .../Patterns/GraphqlDirectivePattern.php | 26 ++++++ .../Graphql/Patterns/GraphqlFieldPattern.php | 26 ++++++ .../Patterns/GraphqlKeywordPattern.php | 26 ++++++ .../Patterns/GraphqlLiteralPattern.php | 26 ++++++ .../Graphql/Patterns/GraphqlNumberPattern.php | 26 ++++++ .../Patterns/GraphqlPunctuationPattern.php | 27 +++++++ .../Graphql/Patterns/GraphqlStringPattern.php | 26 ++++++ .../Patterns/GraphqlVariablePattern.php | 26 ++++++ tests/Bench/Fixtures/graphql.txt | 40 +++++++++ tests/Bench/HighlighterBench.php | 1 + .../Languages/Graphql/GraphqlLanguageTest.php | 81 +++++++++++++++++++ 14 files changed, 441 insertions(+) create mode 100644 src/Languages/Graphql/GraphqlLanguage.php create mode 100644 src/Languages/Graphql/Patterns/GraphqlCommentPattern.php create mode 100644 src/Languages/Graphql/Patterns/GraphqlDirectivePattern.php create mode 100644 src/Languages/Graphql/Patterns/GraphqlFieldPattern.php create mode 100644 src/Languages/Graphql/Patterns/GraphqlKeywordPattern.php create mode 100644 src/Languages/Graphql/Patterns/GraphqlLiteralPattern.php create mode 100644 src/Languages/Graphql/Patterns/GraphqlNumberPattern.php create mode 100644 src/Languages/Graphql/Patterns/GraphqlPunctuationPattern.php create mode 100644 src/Languages/Graphql/Patterns/GraphqlStringPattern.php create mode 100644 src/Languages/Graphql/Patterns/GraphqlVariablePattern.php create mode 100644 tests/Bench/Fixtures/graphql.txt create mode 100644 tests/Languages/Graphql/GraphqlLanguageTest.php diff --git a/src/Highlighter.php b/src/Highlighter.php index fc154e6..56e6bb8 100644 --- a/src/Highlighter.php +++ b/src/Highlighter.php @@ -17,6 +17,7 @@ use Tempest\Highlight\Languages\DotEnv\DotEnvLanguage; use Tempest\Highlight\Languages\Ellison\EllisonLanguage; use Tempest\Highlight\Languages\Gdscript\GdscriptLanguage; +use Tempest\Highlight\Languages\Graphql\GraphqlLanguage; use Tempest\Highlight\Languages\Html\HtmlLanguage; use Tempest\Highlight\Languages\Ini\IniLanguage; use Tempest\Highlight\Languages\JavaScript\JavaScriptLanguage; @@ -69,6 +70,7 @@ public function __construct(private readonly Theme $theme = new CssTheme()) ->addLanguage(new DockerfileLanguage()) ->addLanguage(new EllisonLanguage()) ->addLanguage(new GdscriptLanguage()) + ->addLanguage(new GraphqlLanguage()) ->addLanguage(new HtmlLanguage()) ->addLanguage(new JavaScriptLanguage()) ->addLanguage(new JsonLanguage()) diff --git a/src/Languages/Graphql/GraphqlLanguage.php b/src/Languages/Graphql/GraphqlLanguage.php new file mode 100644 index 0000000..3cdc132 --- /dev/null +++ b/src/Languages/Graphql/GraphqlLanguage.php @@ -0,0 +1,73 @@ +#.*|"""[\s\S]*?""")'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::COMMENT; + } +} diff --git a/src/Languages/Graphql/Patterns/GraphqlDirectivePattern.php b/src/Languages/Graphql/Patterns/GraphqlDirectivePattern.php new file mode 100644 index 0000000..056c72b --- /dev/null +++ b/src/Languages/Graphql/Patterns/GraphqlDirectivePattern.php @@ -0,0 +1,26 @@ +@\w+)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::ATTRIBUTE; + } +} diff --git a/src/Languages/Graphql/Patterns/GraphqlFieldPattern.php b/src/Languages/Graphql/Patterns/GraphqlFieldPattern.php new file mode 100644 index 0000000..184fbf2 --- /dev/null +++ b/src/Languages/Graphql/Patterns/GraphqlFieldPattern.php @@ -0,0 +1,26 @@ +[_A-Za-z][_0-9A-Za-z]*)(?=\s*:)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::PROPERTY; + } +} diff --git a/src/Languages/Graphql/Patterns/GraphqlKeywordPattern.php b/src/Languages/Graphql/Patterns/GraphqlKeywordPattern.php new file mode 100644 index 0000000..008d756 --- /dev/null +++ b/src/Languages/Graphql/Patterns/GraphqlKeywordPattern.php @@ -0,0 +1,26 @@ +\b(query|mutation|subscription|input|schema|implements|type|interface|union|scalar|fragment|enum|on)\b)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::KEYWORD; + } +} diff --git a/src/Languages/Graphql/Patterns/GraphqlLiteralPattern.php b/src/Languages/Graphql/Patterns/GraphqlLiteralPattern.php new file mode 100644 index 0000000..7fdffe7 --- /dev/null +++ b/src/Languages/Graphql/Patterns/GraphqlLiteralPattern.php @@ -0,0 +1,26 @@ +\b(true|false|null|ID|ID!|String|Float|Int|Boolean)\b)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::TYPE; + } +} diff --git a/src/Languages/Graphql/Patterns/GraphqlNumberPattern.php b/src/Languages/Graphql/Patterns/GraphqlNumberPattern.php new file mode 100644 index 0000000..54aeb0b --- /dev/null +++ b/src/Languages/Graphql/Patterns/GraphqlNumberPattern.php @@ -0,0 +1,26 @@ +\b\d+(\.\d+)?)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::NUMBER; + } +} diff --git a/src/Languages/Graphql/Patterns/GraphqlPunctuationPattern.php b/src/Languages/Graphql/Patterns/GraphqlPunctuationPattern.php new file mode 100644 index 0000000..df07b61 --- /dev/null +++ b/src/Languages/Graphql/Patterns/GraphqlPunctuationPattern.php @@ -0,0 +1,27 @@ +\.{3}|[!():=\[\]{|}]|(?([^"\\\\]|\\\\.)*)"'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::VALUE; + } +} diff --git a/src/Languages/Graphql/Patterns/GraphqlVariablePattern.php b/src/Languages/Graphql/Patterns/GraphqlVariablePattern.php new file mode 100644 index 0000000..af1bc8b --- /dev/null +++ b/src/Languages/Graphql/Patterns/GraphqlVariablePattern.php @@ -0,0 +1,26 @@ +\$[\w]+)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::VARIABLE; + } +} diff --git a/tests/Bench/Fixtures/graphql.txt b/tests/Bench/Fixtures/graphql.txt new file mode 100644 index 0000000..a71f966 --- /dev/null +++ b/tests/Bench/Fixtures/graphql.txt @@ -0,0 +1,40 @@ +# Query users by name +query UsersByName($name: String!) { + users($name: $name) { + id + } +} + +# ====== +# Schema +# ====== + +""" +A user +""" +type User { + id: ID! + name: String! +} + +type Query { + """ + Query users by name. + """ + users(name: String): [User!]! + """ + Query a user by ID. + """ + user(id: ID!): User +} + +type Mutation { + """ + Create a user + """ + createUser(input: CreateUserInput!): User +} + +input CreateUserInput { + name: String! +} \ No newline at end of file diff --git a/tests/Bench/HighlighterBench.php b/tests/Bench/HighlighterBench.php index 397d321..8abe8a8 100644 --- a/tests/Bench/HighlighterBench.php +++ b/tests/Bench/HighlighterBench.php @@ -27,6 +27,7 @@ final class HighlighterBench 'dotenv' => 'dotenv.txt', 'ellison' => 'ellison.txt', 'gdscript' => 'gdscript.txt', + 'graphql' => 'graphql.txt', 'html' => 'html.txt', 'ini' => 'ini.txt', 'javascript' => 'javascript.txt', diff --git a/tests/Languages/Graphql/GraphqlLanguageTest.php b/tests/Languages/Graphql/GraphqlLanguageTest.php new file mode 100644 index 0000000..d09c5f9 --- /dev/null +++ b/tests/Languages/Graphql/GraphqlLanguageTest.php @@ -0,0 +1,81 @@ +assertSame( + trim($expected), + trim($highlighter->parse($content, 'graphql')) + ); + } + + public static function provide_highlighting_cases(): iterable + { + return [ + // Test Keywords & Types + [ + 'query GetUser { user { id } }', + 'query GetUser { user { id } }', + ], + // Test Literals & Built-in Types + [ + 'scalar Custom String Boolean', + 'scalar Custom String Boolean', + ], + // Test Variables + [ + 'query($id: ID!)', + 'query($id: ID!)', + ], + // Test Fields (Symbols) + [ + '{ user(name: "Test") }', + '{ user(name: "Test") }', + ], + // Test Directives + [ + 'field @deprecated', + 'field @deprecated', + ], + // Test Comments + [ + '# This is a comment', + '# This is a comment', + ], + [ + '""" + Multi-line + Comment + """ + type User { id: ID }', + '""" + Multi-line + Comment + """ + type User { id: ID }', + ], + // Test Numbers + [ + 'offset: 10, price: 1.99', + 'offset: 10, price: 1.99', + ], + // Test Fragments and Spread + [ + '...UserFragment', + '...UserFragment', + ], + ]; + } +}