From 2401866e5afee0aa67fe5064a9ab41d06ecbde6d Mon Sep 17 00:00:00 2001 From: mazza <128099384+mazza-av@users.noreply.github.com> Date: Sun, 14 May 2023 05:29:03 -0400 Subject: Add support for GraphQL (#2428) Co-authored-by: Jean Abou Samra --- pygments/lexers/_mapping.py | 1 + pygments/lexers/graphql.py | 176 +++++ tests/examplefiles/graphql/ex01_field1.graphql | 5 + .../graphql/ex01_field1.graphql.output | 13 + tests/examplefiles/graphql/ex02_field2.graphql | 9 + .../graphql/ex02_field2.graphql.output | 23 + tests/examplefiles/graphql/ex03_arguments1.graphql | 6 + .../graphql/ex03_arguments1.graphql.output | 23 + tests/examplefiles/graphql/ex04_arguments2.graphql | 6 + .../graphql/ex04_arguments2.graphql.output | 29 + tests/examplefiles/graphql/ex05_aliases.graphql | 8 + .../graphql/ex05_aliases.graphql.output | 39 + tests/examplefiles/graphql/ex06_fragments1.graphql | 16 + .../graphql/ex06_fragments1.graphql.output | 67 ++ tests/examplefiles/graphql/ex07_fragments2.graphql | 20 + .../graphql/ex07_fragments2.graphql.output | 99 +++ .../graphql/ex08_operation_name.graphql | 8 + .../graphql/ex08_operation_name.graphql.output | 25 + tests/examplefiles/graphql/ex09_variables1.graphql | 8 + .../graphql/ex09_variables1.graphql.output | 37 + tests/examplefiles/graphql/ex10_variables2.graphql | 8 + .../graphql/ex10_variables2.graphql.output | 41 + tests/examplefiles/graphql/ex11_directives.graphql | 8 + .../graphql/ex11_directives.graphql.output | 52 ++ tests/examplefiles/graphql/ex12_mutations.graphql | 6 + .../graphql/ex12_mutations.graphql.output | 45 ++ .../graphql/ex13_inline_fragments1.graphql | 11 + .../graphql/ex13_inline_fragments1.graphql.output | 54 ++ .../graphql/ex14_inline_fragments2.graphql | 14 + .../graphql/ex14_inline_fragments2.graphql.output | 57 ++ tests/test_graphql.py | 872 +++++++++++++++++++++ 31 files changed, 1786 insertions(+) create mode 100644 pygments/lexers/graphql.py create mode 100644 tests/examplefiles/graphql/ex01_field1.graphql create mode 100644 tests/examplefiles/graphql/ex01_field1.graphql.output create mode 100644 tests/examplefiles/graphql/ex02_field2.graphql create mode 100644 tests/examplefiles/graphql/ex02_field2.graphql.output create mode 100644 tests/examplefiles/graphql/ex03_arguments1.graphql create mode 100644 tests/examplefiles/graphql/ex03_arguments1.graphql.output create mode 100644 tests/examplefiles/graphql/ex04_arguments2.graphql create mode 100644 tests/examplefiles/graphql/ex04_arguments2.graphql.output create mode 100644 tests/examplefiles/graphql/ex05_aliases.graphql create mode 100644 tests/examplefiles/graphql/ex05_aliases.graphql.output create mode 100644 tests/examplefiles/graphql/ex06_fragments1.graphql create mode 100644 tests/examplefiles/graphql/ex06_fragments1.graphql.output create mode 100644 tests/examplefiles/graphql/ex07_fragments2.graphql create mode 100644 tests/examplefiles/graphql/ex07_fragments2.graphql.output create mode 100644 tests/examplefiles/graphql/ex08_operation_name.graphql create mode 100644 tests/examplefiles/graphql/ex08_operation_name.graphql.output create mode 100644 tests/examplefiles/graphql/ex09_variables1.graphql create mode 100644 tests/examplefiles/graphql/ex09_variables1.graphql.output create mode 100644 tests/examplefiles/graphql/ex10_variables2.graphql create mode 100644 tests/examplefiles/graphql/ex10_variables2.graphql.output create mode 100644 tests/examplefiles/graphql/ex11_directives.graphql create mode 100644 tests/examplefiles/graphql/ex11_directives.graphql.output create mode 100644 tests/examplefiles/graphql/ex12_mutations.graphql create mode 100644 tests/examplefiles/graphql/ex12_mutations.graphql.output create mode 100644 tests/examplefiles/graphql/ex13_inline_fragments1.graphql create mode 100644 tests/examplefiles/graphql/ex13_inline_fragments1.graphql.output create mode 100644 tests/examplefiles/graphql/ex14_inline_fragments2.graphql create mode 100644 tests/examplefiles/graphql/ex14_inline_fragments2.graphql.output create mode 100644 tests/test_graphql.py diff --git a/pygments/lexers/_mapping.py b/pygments/lexers/_mapping.py index 05e9b32f..cb28f176 100644 --- a/pygments/lexers/_mapping.py +++ b/pygments/lexers/_mapping.py @@ -190,6 +190,7 @@ LEXERS = { 'GoodDataCLLexer': ('pygments.lexers.business', 'GoodData-CL', ('gooddata-cl',), ('*.gdc',), ('text/x-gooddata-cl',)), 'GosuLexer': ('pygments.lexers.jvm', 'Gosu', ('gosu',), ('*.gs', '*.gsx', '*.gsp', '*.vark'), ('text/x-gosu',)), 'GosuTemplateLexer': ('pygments.lexers.jvm', 'Gosu Template', ('gst',), ('*.gst',), ('text/x-gosu-template',)), + 'GraphQLLexer': ('pygments.lexers.graphql', 'GraphQL', ('graphql',), ('*.graphql',), ()), 'GraphvizLexer': ('pygments.lexers.graphviz', 'Graphviz', ('graphviz', 'dot'), ('*.gv', '*.dot'), ('text/x-graphviz', 'text/vnd.graphviz')), 'GroffLexer': ('pygments.lexers.markup', 'Groff', ('groff', 'nroff', 'man'), ('*.[1-9]', '*.man', '*.1p', '*.3pm'), ('application/x-troff', 'text/troff')), 'GroovyLexer': ('pygments.lexers.jvm', 'Groovy', ('groovy',), ('*.groovy', '*.gradle'), ('text/x-groovy',)), diff --git a/pygments/lexers/graphql.py b/pygments/lexers/graphql.py new file mode 100644 index 00000000..4c09659b --- /dev/null +++ b/pygments/lexers/graphql.py @@ -0,0 +1,176 @@ +""" + pygments.lexers.graphql + ~~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for GraphQL, an open-source data query and manipulation + language for APIs. + + More information: + https://graphql.org/ + + :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, words, include, bygroups, default +from pygments.token import (Comment, Keyword, Name, Number, Punctuation, String, + Whitespace) + + +__all__ = ["GraphQLLexer"] + +OPERATION_TYPES = ("query", "mutation", "subscription") +BUILTIN_TYPES = ("Int", "Float", "String", "Boolean", "ID") +BOOLEAN_VALUES = ("true", "false", "null") +KEYWORDS = ( + "type", + "schema", + "extend", + "enum", + "scalar", + "implements", + "interface", + "union", + "input", + "directive", + "QUERY", + "MUTATION", + "SUBSCRIPTION", + "FIELD", + "FRAGMENT_DEFINITION", + "FRAGMENT_SPREAD", + "INLINE_FRAGMENT", + "SCHEMA", + "SCALAR", + "OBJECT", + "FIELD_DEFINITION", + "ARGUMENT_DEFINITION", + "INTERFACE", + "UNION", + "ENUM", + "ENUM_VALUE", + "INPUT_OBJECT", + "INPUT_FIELD_DEFINITION", +) + + +class GraphQLLexer(RegexLexer): + """ + Lexer for GraphQL syntax + + .. versionadded:: 2.15.1 + """ + name = "GraphQL" + aliases = ["graphql"] + filenames = ["*.graphql"] + + tokens = { + "ignored_tokens": [ + (r"\s+", Whitespace), # Whitespaces + (r"#.*$", Comment), + (",", Punctuation), # Insignificant commas + ], + "value": [ + include("ignored_tokens"), + (r"-?\d+(?![.eE])", Number.Integer, "#pop"), + ( + r"-?\d+(\.\d+)?([eE][+-]?\d+)?", + Number.Float, + "#pop", + ), + (r'"', String, ("#pop", "string")), + (words(BOOLEAN_VALUES, suffix=r"\b"), Name.Builtin, "#pop"), + (r"\$[a-zA-Z_]\w*", Name.Variable, "#pop"), + (r"[a-zA-Z_]\w*", Name.Constant, "#pop"), + (r"\[", Punctuation, ("#pop", "list_value")), + (r"\{", Punctuation, ("#pop", "object_value")), + ], + "list_value": [ + include("ignored_tokens"), + ("]", Punctuation, "#pop"), + default("value"), + ], + "object_value": [ + include("ignored_tokens"), + (r"[a-zA-Z_]\w*", Name), + (r":", Punctuation, "value"), + (r"\}", Punctuation, "#pop"), + ], + "string": [ + (r'\\(["\\/bfnrt]|u[a-fA-F0-9]{4})', String.Escape), + (r'[^\\"\n]+', String), # all other characters + (r'"', String, "#pop"), + ], + "root": [ + include("ignored_tokens"), + (words(OPERATION_TYPES, suffix=r"\b"), Keyword, "operation"), + (words(KEYWORDS, suffix=r"\b"), Keyword), + (r"\{", Punctuation, "selection_set"), + (r"fragment\b", Keyword, "fragment_definition"), + ], + "operation": [ + include("ignored_tokens"), + (r"[a-zA-Z_]\w*", Name.Function), + (r"\(", Punctuation, "variable_definition"), + (r"\{", Punctuation, ("#pop", "selection_set")), + ], + "variable_definition": [ + include("ignored_tokens"), + (r"\$[a-zA-Z_]\w*", Name.Variable), + (r"[\]!]", Punctuation), + (r":", Punctuation, "type"), + (r"=", Punctuation, "value"), + (r"\)", Punctuation, "#pop"), + ], + "type": [ + include("ignored_tokens"), + (r"\[", Punctuation), + (words(BUILTIN_TYPES, suffix=r"\b"), Name.Builtin, "#pop"), + (r"[a-zA-Z_]\w*", Name.Class, "#pop"), + ], + "selection_set": [ + include("ignored_tokens"), + (r"([a-zA-Z_]\w*)(\s*)(:)", bygroups(Name.Label, Whitespace, Punctuation)), + (r"[a-zA-Z_]\w*", Name), # Field + ( + r"(\.\.\.)(\s+)(on)\b", + bygroups(Punctuation, Whitespace, Keyword), + "inline_fragment", + ), + (r"\.\.\.", Punctuation, "fragment_spread"), + (r"\(", Punctuation, "arguments"), + (r"@[a-zA-Z_]\w*", Name.Decorator, "directive"), + (r"\{", Punctuation, "selection_set"), + (r"\}", Punctuation, "#pop"), + ], + "directive": [ + include("ignored_tokens"), + (r"\(", Punctuation, ("#pop", "arguments")), + ], + "arguments": [ + include("ignored_tokens"), + (r"[a-zA-Z_]\w*", Name), + (r":", Punctuation, "value"), + (r"\)", Punctuation, "#pop"), + ], + # Fragments + "fragment_definition": [ + include("ignored_tokens"), + (r"[\]!]", Punctuation), # For NamedType + (r"on\b", Keyword, "type"), + (r"[a-zA-Z_]\w*", Name.Function), + (r"@[a-zA-Z_]\w*", Name.Decorator, "directive"), + (r"\{", Punctuation, ("#pop", "selection_set")), + ], + "fragment_spread": [ + include("ignored_tokens"), + (r"@[a-zA-Z_]\w*", Name.Decorator, "directive"), + (r"[a-zA-Z_]\w*", Name, "#pop"), # Fragment name + ], + "inline_fragment": [ + include("ignored_tokens"), + (r"[a-zA-Z_]\w*", Name.Class), # Type condition + (r"@[a-zA-Z_]\w*", Name.Decorator, "directive"), + (r"\{", Punctuation, ("#pop", "selection_set")), + ], + } diff --git a/tests/examplefiles/graphql/ex01_field1.graphql b/tests/examplefiles/graphql/ex01_field1.graphql new file mode 100644 index 00000000..d4aef93a --- /dev/null +++ b/tests/examplefiles/graphql/ex01_field1.graphql @@ -0,0 +1,5 @@ +{ + hero { + name + } +} diff --git a/tests/examplefiles/graphql/ex01_field1.graphql.output b/tests/examplefiles/graphql/ex01_field1.graphql.output new file mode 100644 index 00000000..ae18503a --- /dev/null +++ b/tests/examplefiles/graphql/ex01_field1.graphql.output @@ -0,0 +1,13 @@ +'{' Punctuation +'\n ' Text.Whitespace +'hero' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex02_field2.graphql b/tests/examplefiles/graphql/ex02_field2.graphql new file mode 100644 index 00000000..68f1695b --- /dev/null +++ b/tests/examplefiles/graphql/ex02_field2.graphql @@ -0,0 +1,9 @@ +{ + hero { + name + # Queries can have comments! + friends { + name + } + } +} diff --git a/tests/examplefiles/graphql/ex02_field2.graphql.output b/tests/examplefiles/graphql/ex02_field2.graphql.output new file mode 100644 index 00000000..dcdb8cc9 --- /dev/null +++ b/tests/examplefiles/graphql/ex02_field2.graphql.output @@ -0,0 +1,23 @@ +'{' Punctuation +'\n ' Text.Whitespace +'hero' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'# Queries can have comments!' Comment +'\n ' Text.Whitespace +'friends' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex03_arguments1.graphql b/tests/examplefiles/graphql/ex03_arguments1.graphql new file mode 100644 index 00000000..e46bcbe2 --- /dev/null +++ b/tests/examplefiles/graphql/ex03_arguments1.graphql @@ -0,0 +1,6 @@ +{ + human(id: "1000") { + name + height + } +} diff --git a/tests/examplefiles/graphql/ex03_arguments1.graphql.output b/tests/examplefiles/graphql/ex03_arguments1.graphql.output new file mode 100644 index 00000000..78f86964 --- /dev/null +++ b/tests/examplefiles/graphql/ex03_arguments1.graphql.output @@ -0,0 +1,23 @@ +'{' Punctuation +'\n ' Text.Whitespace +'human' Name +'(' Punctuation +'id' Name +':' Punctuation +' ' Text.Whitespace +'"' Literal.String +'1000' Literal.String +'"' Literal.String +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'height' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex04_arguments2.graphql b/tests/examplefiles/graphql/ex04_arguments2.graphql new file mode 100644 index 00000000..f406f637 --- /dev/null +++ b/tests/examplefiles/graphql/ex04_arguments2.graphql @@ -0,0 +1,6 @@ +{ + human(id: "1000") { + name + height(unit: FOOT) + } +} diff --git a/tests/examplefiles/graphql/ex04_arguments2.graphql.output b/tests/examplefiles/graphql/ex04_arguments2.graphql.output new file mode 100644 index 00000000..472a2ed8 --- /dev/null +++ b/tests/examplefiles/graphql/ex04_arguments2.graphql.output @@ -0,0 +1,29 @@ +'{' Punctuation +'\n ' Text.Whitespace +'human' Name +'(' Punctuation +'id' Name +':' Punctuation +' ' Text.Whitespace +'"' Literal.String +'1000' Literal.String +'"' Literal.String +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'height' Name +'(' Punctuation +'unit' Name +':' Punctuation +' ' Text.Whitespace +'FOOT' Name.Constant +')' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex05_aliases.graphql b/tests/examplefiles/graphql/ex05_aliases.graphql new file mode 100644 index 00000000..a21db69e --- /dev/null +++ b/tests/examplefiles/graphql/ex05_aliases.graphql @@ -0,0 +1,8 @@ +{ + empireHero: hero(episode: EMPIRE) { + name + } + jediHero: hero(episode: JEDI) { + name + } +} diff --git a/tests/examplefiles/graphql/ex05_aliases.graphql.output b/tests/examplefiles/graphql/ex05_aliases.graphql.output new file mode 100644 index 00000000..1f4dcead --- /dev/null +++ b/tests/examplefiles/graphql/ex05_aliases.graphql.output @@ -0,0 +1,39 @@ +'{' Punctuation +'\n ' Text.Whitespace +'empireHero' Name.Label +':' Punctuation +' ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'EMPIRE' Name.Constant +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'jediHero' Name.Label +':' Punctuation +' ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'JEDI' Name.Constant +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex06_fragments1.graphql b/tests/examplefiles/graphql/ex06_fragments1.graphql new file mode 100644 index 00000000..7c2783e7 --- /dev/null +++ b/tests/examplefiles/graphql/ex06_fragments1.graphql @@ -0,0 +1,16 @@ +{ + leftComparison: hero(episode: EMPIRE) { + ...comparisonFields + } + rightComparison: hero(episode: JEDI) { + ...comparisonFields + } +} + +fragment comparisonFields on Character { + name + appearsIn + friends { + name + } +} diff --git a/tests/examplefiles/graphql/ex06_fragments1.graphql.output b/tests/examplefiles/graphql/ex06_fragments1.graphql.output new file mode 100644 index 00000000..76c0af07 --- /dev/null +++ b/tests/examplefiles/graphql/ex06_fragments1.graphql.output @@ -0,0 +1,67 @@ +'{' Punctuation +'\n ' Text.Whitespace +'leftComparison' Name.Label +':' Punctuation +' ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'EMPIRE' Name.Constant +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'...' Punctuation +'comparisonFields' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'rightComparison' Name.Label +':' Punctuation +' ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'JEDI' Name.Constant +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'...' Punctuation +'comparisonFields' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n\n' Text.Whitespace + +'fragment' Keyword +' ' Text.Whitespace +'comparisonFields' Name.Function +' ' Text.Whitespace +'on' Keyword +' ' Text.Whitespace +'Character' Name.Class +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'appearsIn' Name +'\n ' Text.Whitespace +'friends' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex07_fragments2.graphql b/tests/examplefiles/graphql/ex07_fragments2.graphql new file mode 100644 index 00000000..f9294954 --- /dev/null +++ b/tests/examplefiles/graphql/ex07_fragments2.graphql @@ -0,0 +1,20 @@ +query HeroComparison($first: Int = 3) { + leftComparison: hero(episode: EMPIRE) { + ...comparisonFields + } + rightComparison: hero(episode: JEDI) { + ...comparisonFields + } +} + +fragment comparisonFields on Character { + name + friendsConnection(first: $first) { + totalCount + edges { + node { + name + } + } + } +} diff --git a/tests/examplefiles/graphql/ex07_fragments2.graphql.output b/tests/examplefiles/graphql/ex07_fragments2.graphql.output new file mode 100644 index 00000000..2ee8e6cd --- /dev/null +++ b/tests/examplefiles/graphql/ex07_fragments2.graphql.output @@ -0,0 +1,99 @@ +'query' Keyword +' ' Text.Whitespace +'HeroComparison' Name.Function +'(' Punctuation +'$first' Name.Variable +':' Punctuation +' ' Text.Whitespace +'Int' Name.Builtin +' ' Text.Whitespace +'=' Punctuation +' ' Text.Whitespace +'3' Literal.Number.Integer +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'leftComparison' Name.Label +':' Punctuation +' ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'EMPIRE' Name.Constant +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'...' Punctuation +'comparisonFields' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'rightComparison' Name.Label +':' Punctuation +' ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'JEDI' Name.Constant +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'...' Punctuation +'comparisonFields' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n\n' Text.Whitespace + +'fragment' Keyword +' ' Text.Whitespace +'comparisonFields' Name.Function +' ' Text.Whitespace +'on' Keyword +' ' Text.Whitespace +'Character' Name.Class +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'friendsConnection' Name +'(' Punctuation +'first' Name +':' Punctuation +' ' Text.Whitespace +'$first' Name.Variable +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'totalCount' Name +'\n ' Text.Whitespace +'edges' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'node' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex08_operation_name.graphql b/tests/examplefiles/graphql/ex08_operation_name.graphql new file mode 100644 index 00000000..13c21a62 --- /dev/null +++ b/tests/examplefiles/graphql/ex08_operation_name.graphql @@ -0,0 +1,8 @@ +query HeroNameAndFriends { + hero { + name + friends { + name + } + } +} diff --git a/tests/examplefiles/graphql/ex08_operation_name.graphql.output b/tests/examplefiles/graphql/ex08_operation_name.graphql.output new file mode 100644 index 00000000..eacc529e --- /dev/null +++ b/tests/examplefiles/graphql/ex08_operation_name.graphql.output @@ -0,0 +1,25 @@ +'query' Keyword +' ' Text.Whitespace +'HeroNameAndFriends' Name.Function +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'hero' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'friends' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex09_variables1.graphql b/tests/examplefiles/graphql/ex09_variables1.graphql new file mode 100644 index 00000000..5f0f4ef1 --- /dev/null +++ b/tests/examplefiles/graphql/ex09_variables1.graphql @@ -0,0 +1,8 @@ +query HeroNameAndFriends($episode: Episode) { + hero(episode: $episode) { + name + friends { + name + } + } +} diff --git a/tests/examplefiles/graphql/ex09_variables1.graphql.output b/tests/examplefiles/graphql/ex09_variables1.graphql.output new file mode 100644 index 00000000..8d6bdd9f --- /dev/null +++ b/tests/examplefiles/graphql/ex09_variables1.graphql.output @@ -0,0 +1,37 @@ +'query' Keyword +' ' Text.Whitespace +'HeroNameAndFriends' Name.Function +'(' Punctuation +'$episode' Name.Variable +':' Punctuation +' ' Text.Whitespace +'Episode' Name.Class +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'$episode' Name.Variable +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'friends' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex10_variables2.graphql b/tests/examplefiles/graphql/ex10_variables2.graphql new file mode 100644 index 00000000..3db6cc71 --- /dev/null +++ b/tests/examplefiles/graphql/ex10_variables2.graphql @@ -0,0 +1,8 @@ +query HeroNameAndFriends($episode: Episode = JEDI) { + hero(episode: $episode) { + name + friends { + name + } + } +} diff --git a/tests/examplefiles/graphql/ex10_variables2.graphql.output b/tests/examplefiles/graphql/ex10_variables2.graphql.output new file mode 100644 index 00000000..751cdc8a --- /dev/null +++ b/tests/examplefiles/graphql/ex10_variables2.graphql.output @@ -0,0 +1,41 @@ +'query' Keyword +' ' Text.Whitespace +'HeroNameAndFriends' Name.Function +'(' Punctuation +'$episode' Name.Variable +':' Punctuation +' ' Text.Whitespace +'Episode' Name.Class +' ' Text.Whitespace +'=' Punctuation +' ' Text.Whitespace +'JEDI' Name.Constant +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'$episode' Name.Variable +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'friends' Name +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex11_directives.graphql b/tests/examplefiles/graphql/ex11_directives.graphql new file mode 100644 index 00000000..9f0b76f1 --- /dev/null +++ b/tests/examplefiles/graphql/ex11_directives.graphql @@ -0,0 +1,8 @@ +query Hero($episode: Episode, $withFriends: Boolean!) { + hero(episode: $episode) { + name + friends @include(if: $withFriends) { + name + } + } +} diff --git a/tests/examplefiles/graphql/ex11_directives.graphql.output b/tests/examplefiles/graphql/ex11_directives.graphql.output new file mode 100644 index 00000000..486b87c4 --- /dev/null +++ b/tests/examplefiles/graphql/ex11_directives.graphql.output @@ -0,0 +1,52 @@ +'query' Keyword +' ' Text.Whitespace +'Hero' Name.Function +'(' Punctuation +'$episode' Name.Variable +':' Punctuation +' ' Text.Whitespace +'Episode' Name.Class +',' Punctuation +' ' Text.Whitespace +'$withFriends' Name.Variable +':' Punctuation +' ' Text.Whitespace +'Boolean' Name.Builtin +'!' Punctuation +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'$episode' Name.Variable +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'friends' Name +' ' Text.Whitespace +'@include' Name.Decorator +'(' Punctuation +'if' Name +':' Punctuation +' ' Text.Whitespace +'$withFriends' Name.Variable +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex12_mutations.graphql b/tests/examplefiles/graphql/ex12_mutations.graphql new file mode 100644 index 00000000..c82d383d --- /dev/null +++ b/tests/examplefiles/graphql/ex12_mutations.graphql @@ -0,0 +1,6 @@ +mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { + createReview(episode: $ep, review: $review) { + stars + commentary + } +} diff --git a/tests/examplefiles/graphql/ex12_mutations.graphql.output b/tests/examplefiles/graphql/ex12_mutations.graphql.output new file mode 100644 index 00000000..5d6def44 --- /dev/null +++ b/tests/examplefiles/graphql/ex12_mutations.graphql.output @@ -0,0 +1,45 @@ +'mutation' Keyword +' ' Text.Whitespace +'CreateReviewForEpisode' Name.Function +'(' Punctuation +'$ep' Name.Variable +':' Punctuation +' ' Text.Whitespace +'Episode' Name.Class +'!' Punctuation +',' Punctuation +' ' Text.Whitespace +'$review' Name.Variable +':' Punctuation +' ' Text.Whitespace +'ReviewInput' Name.Class +'!' Punctuation +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'createReview' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'$ep' Name.Variable +',' Punctuation +' ' Text.Whitespace +'review' Name +':' Punctuation +' ' Text.Whitespace +'$review' Name.Variable +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'stars' Name +'\n ' Text.Whitespace +'commentary' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex13_inline_fragments1.graphql b/tests/examplefiles/graphql/ex13_inline_fragments1.graphql new file mode 100644 index 00000000..df261926 --- /dev/null +++ b/tests/examplefiles/graphql/ex13_inline_fragments1.graphql @@ -0,0 +1,11 @@ +query HeroForEpisode($ep: Episode!) { + hero(episode: $ep) { + name + ... on Droid { + primaryFunction + } + ... on Human { + height + } + } +} diff --git a/tests/examplefiles/graphql/ex13_inline_fragments1.graphql.output b/tests/examplefiles/graphql/ex13_inline_fragments1.graphql.output new file mode 100644 index 00000000..16348903 --- /dev/null +++ b/tests/examplefiles/graphql/ex13_inline_fragments1.graphql.output @@ -0,0 +1,54 @@ +'query' Keyword +' ' Text.Whitespace +'HeroForEpisode' Name.Function +'(' Punctuation +'$ep' Name.Variable +':' Punctuation +' ' Text.Whitespace +'Episode' Name.Class +'!' Punctuation +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'hero' Name +'(' Punctuation +'episode' Name +':' Punctuation +' ' Text.Whitespace +'$ep' Name.Variable +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'...' Punctuation +' ' Text.Whitespace +'on' Keyword +' ' Text.Whitespace +'Droid' Name.Class +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'primaryFunction' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'...' Punctuation +' ' Text.Whitespace +'on' Keyword +' ' Text.Whitespace +'Human' Name.Class +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'height' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/examplefiles/graphql/ex14_inline_fragments2.graphql b/tests/examplefiles/graphql/ex14_inline_fragments2.graphql new file mode 100644 index 00000000..f756edc4 --- /dev/null +++ b/tests/examplefiles/graphql/ex14_inline_fragments2.graphql @@ -0,0 +1,14 @@ +{ + search(text: "an") { + __typename + ... on Human { + name + } + ... on Droid { + name + } + ... on Starship { + name + } + } +} diff --git a/tests/examplefiles/graphql/ex14_inline_fragments2.graphql.output b/tests/examplefiles/graphql/ex14_inline_fragments2.graphql.output new file mode 100644 index 00000000..2cfca433 --- /dev/null +++ b/tests/examplefiles/graphql/ex14_inline_fragments2.graphql.output @@ -0,0 +1,57 @@ +'{' Punctuation +'\n ' Text.Whitespace +'search' Name +'(' Punctuation +'text' Name +':' Punctuation +' ' Text.Whitespace +'"' Literal.String +'an' Literal.String +'"' Literal.String +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'__typename' Name +'\n ' Text.Whitespace +'...' Punctuation +' ' Text.Whitespace +'on' Keyword +' ' Text.Whitespace +'Human' Name.Class +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'...' Punctuation +' ' Text.Whitespace +'on' Keyword +' ' Text.Whitespace +'Droid' Name.Class +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'...' Punctuation +' ' Text.Whitespace +'on' Keyword +' ' Text.Whitespace +'Starship' Name.Class +' ' Text.Whitespace +'{' Punctuation +'\n ' Text.Whitespace +'name' Name +'\n ' Text.Whitespace +'}' Punctuation +'\n ' Text.Whitespace +'}' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace diff --git a/tests/test_graphql.py b/tests/test_graphql.py new file mode 100644 index 00000000..8e64f68a --- /dev/null +++ b/tests/test_graphql.py @@ -0,0 +1,872 @@ +import unittest +import textwrap + +from pygments.lexers import GraphQLLexer +from pygments.token import ( + Comment, + Error, + Keyword, + Name, + Number, + Punctuation, + String, + Whitespace +) + + +def assert_tokens_equal(self, text, expected, stack=("root",)): + text = textwrap.dedent(text) + lexer = GraphQLLexer() + tokens_itr = lexer.get_tokens_unprocessed(text, stack=stack) + self.assertEqual(list(tokens_itr), expected) + + +def assert_value_equal(self, text, expected): + if not isinstance(expected, list): + expected = [expected] + assert_tokens_equal(self, text, expected, stack=("value",)) + + +def assert_string_equal(self, text, tokens): + start = (0, String, '"') + end = (len(text) - 1, String, '"') + expected = [start, *tokens, end] + assert_tokens_equal(self, text, expected, stack=("value",)) + + +class TestValue(unittest.TestCase): + + maxDiff = None + + def test_float(self): + assert_value_equal(self, "5.5", (0, Number.Float, "5.5")) + assert_value_equal(self, "5e3", (0, Number.Float, "5e3")) + assert_value_equal(self, "5E3", (0, Number.Float, "5E3")) + assert_value_equal(self, "5e-3", (0, Number.Float, "5e-3")) + assert_value_equal(self, "5E-3", (0, Number.Float, "5E-3")) + assert_value_equal(self, "5e+3", (0, Number.Float, "5e+3")) + assert_value_equal(self, "5E+3", (0, Number.Float, "5E+3")) + assert_value_equal(self, "5.1e3", (0, Number.Float, "5.1e3")) + assert_value_equal(self, "5.1E3", (0, Number.Float, "5.1E3")) + assert_value_equal(self, "5.1e-3", (0, Number.Float, "5.1e-3")) + assert_value_equal(self, "5.1E-3", (0, Number.Float, "5.1E-3")) + assert_value_equal(self, "5.1e+3", (0, Number.Float, "5.1e+3")) + assert_value_equal(self, "5.1E+3", (0, Number.Float, "5.1E+3")) + + def test_integer(self): + assert_value_equal(self, "5", (0, Number.Integer, "5")) + assert_value_equal(self, "-0", (0, Number.Integer, "-0")) + # Not really following the specifications + assert_value_equal(self, "-02", (0, Number.Integer, "-02")) + + def test_string(self): + assert_string_equal(self, '"asdf"', [(1, String, "asdf")]) + assert_string_equal( + self, + r'"asdf\""', + [ + (1, String, "asdf"), + (5, String.Escape, r"\""), + ], + ) + assert_string_equal( + self, r'"asdf\m"', [(1, String, "asdf"), (5, Error, "\\"), (6, String, "m")] + ) + assert_string_equal( + self, + r'"asdf\u00E8"', + [ + (1, String, "asdf"), + (5, String.Escape, r"\u00E8"), + ], + ) + + def test_boolean(self): + assert_value_equal(self, "true", (0, Name.Builtin, "true")) + assert_value_equal(self, "false", (0, Name.Builtin, "false")) + + def test_variable(self): + assert_value_equal(self, "$hello", (0, Name.Variable, "$hello")) + assert_value_equal( + self, + "$0hello", + [ + (0, Error, "$"), + (1, Number.Integer, "0"), + (2, Name.Constant, "hello"), + ], + ) + + def test_list_value(self): + assert_value_equal( + self, + '[1, 1.1, "abc"]', + [ + (0, Punctuation, "["), + (1, Number.Integer, "1"), + (2, Punctuation, ","), + (3, Whitespace, " "), + (4, Number.Float, "1.1"), + (7, Punctuation, ","), + (8, Whitespace, " "), + (9, String, '"'), + (10, String, "abc"), + (13, String, '"'), + (14, Punctuation, "]"), + ], + ) + assert_value_equal( + self, + '[1, 1.1, ["abc"]]', + [ + (0, Punctuation, "["), + (1, Number.Integer, "1"), + (2, Punctuation, ","), + (3, Whitespace, " "), + (4, Number.Float, "1.1"), + (7, Punctuation, ","), + (8, Whitespace, " "), + (9, Punctuation, "["), + (10, String, '"'), + (11, String, "abc"), + (14, String, '"'), + (15, Punctuation, "]"), + (16, Punctuation, "]"), + ], + ) + + +class TestGraphqlLexer(unittest.TestCase): + + maxDiff = None + + def assert_builtin(self, text): + assert_tokens_equal(self, text, [(0, Name.Builtin, text)], stack=("builtins",)) + + def test_keyworkd(self): + assert_tokens_equal(self, "type", [(0, Keyword, "type")]) + assert_tokens_equal(self, "schema", [(0, Keyword, "schema")]) + assert_tokens_equal(self, "extend", [(0, Keyword, "extend")]) + assert_tokens_equal(self, "enum", [(0, Keyword, "enum")]) + assert_tokens_equal(self, "scalar", [(0, Keyword, "scalar")]) + assert_tokens_equal(self, "implements", [(0, Keyword, "implements")]) + assert_tokens_equal(self, "interface", [(0, Keyword, "interface")]) + assert_tokens_equal(self, "union", [(0, Keyword, "union")]) + assert_tokens_equal(self, "input", [(0, Keyword, "input")]) + assert_tokens_equal(self, "directive", [(0, Keyword, "directive")]) + + def test_arguments_integer(self): + text = """\ + { + field(arg: 24) + field + } + """ + + expected = [ + (0, Punctuation, "{"), + (1, Whitespace, "\n "), + (4, Name, "field"), + (9, Punctuation, "("), + (10, Name, "arg"), + # (10, Name.Attribute, 'arg'), + (13, Punctuation, ":"), + (14, Whitespace, " "), + (15, Number.Integer, "24"), + (17, Punctuation, ")"), + (18, Whitespace, "\n "), + (21, Name, "field"), + (26, Whitespace, "\n"), + (27, Punctuation, "}"), + (28, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_arguments(self): + text = """\ + { + human(id: "1000") { + name + height + } + } + """ + expected = [ + (0, Punctuation, "{"), + (1, Whitespace, "\n "), + (4, Name, "human"), + (9, Punctuation, "("), + # (10, Name.Attribute, 'id'), + (10, Name, "id"), + (12, Punctuation, ":"), + (13, Whitespace, " "), + (14, String, '"'), + (15, String, "1000"), + (19, String, '"'), + (20, Punctuation, ")"), + (21, Whitespace, " "), + (22, Punctuation, "{"), + (23, Whitespace, "\n "), + (28, Name, "name"), + (32, Whitespace, "\n "), + (37, Name, "height"), + (43, Whitespace, "\n "), + (46, Punctuation, "}"), + (47, Whitespace, "\n"), + (48, Punctuation, "}"), + (49, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_argument_scalar(self): + text = """\ + { + human(id: "1000") { + name + height(unit: FOOT) + } + } + """ + expected = [ + (0, Punctuation, "{"), + (1, Whitespace, "\n "), + (4, Name, "human"), + (9, Punctuation, "("), + # (10, Name.Attribute, 'id'), + (10, Name, "id"), + (12, Punctuation, ":"), + (13, Whitespace, " "), + (14, String, '"'), + (15, String, "1000"), + (19, String, '"'), + (20, Punctuation, ")"), + (21, Whitespace, " "), + (22, Punctuation, "{"), + (23, Whitespace, "\n "), + (28, Name, "name"), + (32, Whitespace, "\n "), + (37, Name, "height"), + (43, Punctuation, "("), + # (44, Name.Attribute, 'unit'), + (44, Name, "unit"), + (48, Punctuation, ":"), + (49, Whitespace, " "), + (50, Name.Constant, "FOOT"), + (54, Punctuation, ")"), + (55, Whitespace, "\n "), + (58, Punctuation, "}"), + (59, Whitespace, "\n"), + (60, Punctuation, "}"), + (61, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_alias(self): + text = """\ + { + empireHero: hero(episode: EMPIRE) { + name + } + jediHero: hero(episode: JEDI) { + name + } + } + """ + expected = [ + (0, Punctuation, "{"), + (1, Whitespace, "\n "), + (4, Name.Label, "empireHero"), + (14, Punctuation, ":"), + (15, Whitespace, " "), + (16, Name, "hero"), + (20, Punctuation, "("), + # (21, Name.Attribute, 'episode'), + (21, Name, "episode"), + (28, Punctuation, ":"), + (29, Whitespace, " "), + (30, Name.Constant, "EMPIRE"), + (36, Punctuation, ")"), + (37, Whitespace, " "), + (38, Punctuation, "{"), + (39, Whitespace, "\n "), + (44, Name, "name"), + (48, Whitespace, "\n "), + (51, Punctuation, "}"), + (52, Whitespace, "\n "), + (55, Name.Label, "jediHero"), + (63, Punctuation, ":"), + (64, Whitespace, " "), + (65, Name, "hero"), + (69, Punctuation, "("), + # (70, Name.Attribute, 'episode'), + (70, Name, "episode"), + (77, Punctuation, ":"), + (78, Whitespace, " "), + (79, Name.Constant, "JEDI"), + (83, Punctuation, ")"), + (84, Whitespace, " "), + (85, Punctuation, "{"), + (86, Whitespace, "\n "), + (91, Name, "name"), + (95, Whitespace, "\n "), + (98, Punctuation, "}"), + (99, Whitespace, "\n"), + (100, Punctuation, "}"), + (101, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_variables(self): + text = """\ + mutation testVar($int: Int, $myType: MyType, + $required: String!, $list: [Int!]!) + { + doSomenthing(int: $int, myType: $myType, + required: $required, list: $list) + { + response + } + } + """ + expected = [ + (0, Keyword, "mutation"), + (8, Whitespace, " "), + (9, Name.Function, "testVar"), + (16, Punctuation, "("), + (17, Name.Variable, "$int"), + (21, Punctuation, ":"), + (22, Whitespace, " "), + (23, Name.Builtin, "Int"), + (26, Punctuation, ","), + (27, Whitespace, " "), + (28, Name.Variable, "$myType"), + (35, Punctuation, ":"), + (36, Whitespace, " "), + (37, Name.Class, "MyType"), + (43, Punctuation, ","), + (44, Whitespace, "\n "), + (49, Name.Variable, "$required"), + (58, Punctuation, ":"), + (59, Whitespace, " "), + (60, Name.Builtin, "String"), + (66, Punctuation, "!"), + (67, Punctuation, ","), + (68, Whitespace, " "), + (69, Name.Variable, "$list"), + (74, Punctuation, ":"), + (75, Whitespace, " "), + (76, Punctuation, "["), + (77, Name.Builtin, "Int"), + (80, Punctuation, "!"), + (81, Punctuation, "]"), + (82, Punctuation, "!"), + (83, Punctuation, ")"), + (84, Whitespace, "\n"), + (85, Punctuation, "{"), + (86, Whitespace, "\n "), + (89, Name, "doSomenthing"), + (101, Punctuation, "("), + (102, Name, "int"), + (105, Punctuation, ":"), + (106, Whitespace, " "), + (107, Name.Variable, "$int"), + (111, Punctuation, ","), + (112, Whitespace, " "), + (113, Name, "myType"), + (119, Punctuation, ":"), + (120, Whitespace, " "), + (121, Name.Variable, "$myType"), + (128, Punctuation, ","), + (129, Whitespace, "\n "), + (136, Name, "required"), + (144, Punctuation, ":"), + (145, Whitespace, " "), + (146, Name.Variable, "$required"), + (155, Punctuation, ","), + (156, Whitespace, " "), + (157, Name, "list"), + (161, Punctuation, ":"), + (162, Whitespace, " "), + (163, Name.Variable, "$list"), + (168, Punctuation, ")"), + (169, Whitespace, "\n "), + (172, Punctuation, "{"), + (173, Whitespace, "\n "), + (178, Name, "response"), + (186, Whitespace, "\n "), + (189, Punctuation, "}"), + (190, Whitespace, "\n"), + (191, Punctuation, "}"), + (192, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_operation_definition_directives(self): + text = """\ + query ($foo: Boolean = true, $bar: Boolean = false) { + field @skip(if: $foo) { + subfieldA + } + field @skip(if: $bar) { + subfieldB + } + } + """ + expected = [ + (0, Keyword, "query"), + (5, Whitespace, " "), + (6, Punctuation, "("), + (7, Name.Variable, "$foo"), + (11, Punctuation, ":"), + (12, Whitespace, " "), + (13, Name.Builtin, "Boolean"), + (20, Whitespace, " "), + (21, Punctuation, "="), + (22, Whitespace, " "), + (23, Name.Builtin, "true"), + (27, Punctuation, ","), + (28, Whitespace, " "), + (29, Name.Variable, "$bar"), + (33, Punctuation, ":"), + (34, Whitespace, " "), + (35, Name.Builtin, "Boolean"), + (42, Whitespace, " "), + (43, Punctuation, "="), + (44, Whitespace, " "), + (45, Name.Builtin, "false"), + (50, Punctuation, ")"), + (51, Whitespace, " "), + (52, Punctuation, "{"), + (53, Whitespace, "\n "), + (56, Name, "field"), + (61, Whitespace, " "), + (62, Name.Decorator, "@skip"), + (67, Punctuation, "("), + (68, Name, "if"), + (70, Punctuation, ":"), + (71, Whitespace, " "), + (72, Name.Variable, "$foo"), + (76, Punctuation, ")"), + (77, Whitespace, " "), + (78, Punctuation, "{"), + (79, Whitespace, "\n "), + (84, Name, "subfieldA"), + (93, Whitespace, "\n "), + (96, Punctuation, "}"), + (97, Whitespace, "\n "), + (100, Name, "field"), + (105, Whitespace, " "), + (106, Name.Decorator, "@skip"), + (111, Punctuation, "("), + (112, Name, "if"), + (114, Punctuation, ":"), + (115, Whitespace, " "), + (116, Name.Variable, "$bar"), + (120, Punctuation, ")"), + (121, Whitespace, " "), + (122, Punctuation, "{"), + (123, Whitespace, "\n "), + (128, Name, "subfieldB"), + (137, Whitespace, "\n "), + (140, Punctuation, "}"), + (141, Whitespace, "\n"), + (142, Punctuation, "}"), + (143, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_operation_definition_multi_directives(self): + text = """\ + query ($foo: Boolean = true, $bar: Boolean = false) { + field @skip(if: $foo) @skip(if: $bar) + } + """ + expected = [ + (0, Keyword, "query"), + (5, Whitespace, " "), + (6, Punctuation, "("), + (7, Name.Variable, "$foo"), + (11, Punctuation, ":"), + (12, Whitespace, " "), + (13, Name.Builtin, "Boolean"), + (20, Whitespace, " "), + (21, Punctuation, "="), + (22, Whitespace, " "), + (23, Name.Builtin, "true"), + (27, Punctuation, ","), + (28, Whitespace, " "), + (29, Name.Variable, "$bar"), + (33, Punctuation, ":"), + (34, Whitespace, " "), + (35, Name.Builtin, "Boolean"), + (42, Whitespace, " "), + (43, Punctuation, "="), + (44, Whitespace, " "), + (45, Name.Builtin, "false"), + (50, Punctuation, ")"), + (51, Whitespace, " "), + (52, Punctuation, "{"), + (53, Whitespace, "\n "), + (56, Name, "field"), + (61, Whitespace, " "), + (62, Name.Decorator, "@skip"), + (67, Punctuation, "("), + (68, Name, "if"), + (70, Punctuation, ":"), + (71, Whitespace, " "), + (72, Name.Variable, "$foo"), + (76, Punctuation, ")"), + (77, Whitespace, " "), + (78, Name.Decorator, "@skip"), + (83, Punctuation, "("), + (84, Name, "if"), + (86, Punctuation, ":"), + (87, Whitespace, " "), + (88, Name.Variable, "$bar"), + (92, Punctuation, ")"), + (93, Whitespace, "\n"), + (94, Punctuation, "}"), + (95, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_fragment_spread(self): + text = """\ + query withNestedFragments { + user(id: 4) { + friends(first: 10) { + ...friendFields + } + mutualFriends(first: 10) { + ...friendFields @skip(if: true) + } + } + } + """ + expected = [ + (0, Keyword, "query"), + (5, Whitespace, " "), + (6, Name.Function, "withNestedFragments"), + (25, Whitespace, " "), + (26, Punctuation, "{"), + (27, Whitespace, "\n "), + (30, Name, "user"), + (34, Punctuation, "("), + (35, Name, "id"), + (37, Punctuation, ":"), + (38, Whitespace, " "), + (39, Number.Integer, "4"), + (40, Punctuation, ")"), + (41, Whitespace, " "), + (42, Punctuation, "{"), + (43, Whitespace, "\n "), + (48, Name, "friends"), + (55, Punctuation, "("), + (56, Name, "first"), + (61, Punctuation, ":"), + (62, Whitespace, " "), + (63, Number.Integer, "10"), + (65, Punctuation, ")"), + (66, Whitespace, " "), + (67, Punctuation, "{"), + (68, Whitespace, "\n "), + (75, Punctuation, "..."), + (78, Name, "friendFields"), + (90, Whitespace, "\n "), + (95, Punctuation, "}"), + (96, Whitespace, "\n "), + (101, Name, "mutualFriends"), + (114, Punctuation, "("), + (115, Name, "first"), + (120, Punctuation, ":"), + (121, Whitespace, " "), + (122, Number.Integer, "10"), + (124, Punctuation, ")"), + (125, Whitespace, " "), + (126, Punctuation, "{"), + (127, Whitespace, "\n "), + (134, Punctuation, "..."), + (137, Name, "friendFields"), + (149, Whitespace, " "), + (150, Name.Decorator, "@skip"), + (155, Punctuation, "("), + (156, Name, "if"), + (158, Punctuation, ":"), + (159, Whitespace, " "), + (160, Name.Builtin, "true"), + (164, Punctuation, ")"), + (165, Whitespace, "\n "), + (170, Punctuation, "}"), + (171, Whitespace, "\n "), + (174, Punctuation, "}"), + (175, Whitespace, "\n"), + (176, Punctuation, "}"), + (177, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_inline_fragment(self): + text = """\ + query inlineFragmentTyping { + profiles(handles: ["zuck", "cocacola"]) { + handle + ... on User { + friends { + count + } + } + ... on Page { + likers { + count + } + } + } + } + """ + expected = [ + (0, Keyword, "query"), + (5, Whitespace, " "), + (6, Name.Function, "inlineFragmentTyping"), + (26, Whitespace, " "), + (27, Punctuation, "{"), + (28, Whitespace, "\n "), + (31, Name, "profiles"), + (39, Punctuation, "("), + (40, Name, "handles"), + (47, Punctuation, ":"), + (48, Whitespace, " "), + (49, Punctuation, "["), + (50, String, '"'), + (51, String, "zuck"), + (55, String, '"'), + (56, Punctuation, ","), + (57, Whitespace, " "), + (58, String, '"'), + (59, String, "cocacola"), + (67, String, '"'), + (68, Punctuation, "]"), + (69, Punctuation, ")"), + (70, Whitespace, " "), + (71, Punctuation, "{"), + (72, Whitespace, "\n "), + (77, Name, "handle"), + (83, Whitespace, "\n "), + (88, Punctuation, "..."), + (91, Whitespace, " "), + (92, Keyword, "on"), + (94, Whitespace, " "), + (95, Name.Class, "User"), + (99, Whitespace, " "), + (100, Punctuation, "{"), + (101, Whitespace, "\n "), + (108, Name, "friends"), + (115, Whitespace, " "), + (116, Punctuation, "{"), + (117, Whitespace, "\n "), + (126, Name, "count"), + (131, Whitespace, "\n "), + (138, Punctuation, "}"), + (139, Whitespace, "\n "), + (144, Punctuation, "}"), + (145, Whitespace, "\n "), + (150, Punctuation, "..."), + (153, Whitespace, " "), + (154, Keyword, "on"), + (156, Whitespace, " "), + (157, Name.Class, "Page"), + (161, Whitespace, " "), + (162, Punctuation, "{"), + (163, Whitespace, "\n "), + (170, Name, "likers"), + (176, Whitespace, " "), + (177, Punctuation, "{"), + (178, Whitespace, "\n "), + (187, Name, "count"), + (192, Whitespace, "\n "), + (199, Punctuation, "}"), + (200, Whitespace, "\n "), + (205, Punctuation, "}"), + (206, Whitespace, "\n "), + (209, Punctuation, "}"), + (210, Whitespace, "\n"), + (211, Punctuation, "}"), + (212, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_input_value_nested_array(self): + text = """\ + { + node(arg: [1, [2, 3]]) + } + """ + expected = [ + (0, Punctuation, "{"), + (1, Whitespace, "\n "), + (4, Name, "node"), + (8, Punctuation, "("), + (9, Name, "arg"), + (12, Punctuation, ":"), + (13, Whitespace, " "), + (14, Punctuation, "["), + (15, Number.Integer, "1"), + (16, Punctuation, ","), + (17, Whitespace, " "), + (18, Punctuation, "["), + (19, Number.Integer, "2"), + (20, Punctuation, ","), + (21, Whitespace, " "), + (22, Number.Integer, "3"), + (23, Punctuation, "]"), + (24, Punctuation, "]"), + (25, Punctuation, ")"), + (26, Whitespace, "\n"), + (27, Punctuation, "}"), + (28, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_input_value_object(self): + text = """\ + { + node(arg: {a: "a", b: "b"}) + } + """ + expected = [ + (0, Punctuation, "{"), + (1, Whitespace, "\n "), + (4, Name, "node"), + (8, Punctuation, "("), + (9, Name, "arg"), + (12, Punctuation, ":"), + (13, Whitespace, " "), + (14, Punctuation, "{"), + (15, Name, "a"), + (16, Punctuation, ":"), + (17, Whitespace, " "), + (18, String, '"'), + (19, String, "a"), + (20, String, '"'), + (21, Punctuation, ","), + (22, Whitespace, " "), + (23, Name, "b"), + (24, Punctuation, ":"), + (25, Whitespace, " "), + (26, String, '"'), + (27, String, "b"), + (28, String, '"'), + (29, Punctuation, "}"), + (30, Punctuation, ")"), + (31, Whitespace, "\n"), + (32, Punctuation, "}"), + (33, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_input_nested_value_object(self): + text = """\ + { + node(arg: {a: "a", b: {c: "c", d: {e: "e"}}}) + } + """ + expected = [ + (0, Punctuation, "{"), + (1, Whitespace, "\n "), + (4, Name, "node"), + (8, Punctuation, "("), + (9, Name, "arg"), + (12, Punctuation, ":"), + (13, Whitespace, " "), + (14, Punctuation, "{"), + (15, Name, "a"), + (16, Punctuation, ":"), + (17, Whitespace, " "), + (18, String, '"'), + (19, String, "a"), + (20, String, '"'), + (21, Punctuation, ","), + (22, Whitespace, " "), + (23, Name, "b"), + (24, Punctuation, ":"), + (25, Whitespace, " "), + (26, Punctuation, "{"), + (27, Name, "c"), + (28, Punctuation, ":"), + (29, Whitespace, " "), + (30, String, '"'), + (31, String, "c"), + (32, String, '"'), + (33, Punctuation, ","), + (34, Whitespace, " "), + (35, Name, "d"), + (36, Punctuation, ":"), + (37, Whitespace, " "), + (38, Punctuation, "{"), + (39, Name, "e"), + (40, Punctuation, ":"), + (41, Whitespace, " "), + (42, String, '"'), + (43, String, "e"), + (44, String, '"'), + (45, Punctuation, "}"), + (46, Punctuation, "}"), + (47, Punctuation, "}"), + (48, Punctuation, ")"), + (49, Whitespace, "\n"), + (50, Punctuation, "}"), + (51, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) + + def test_input_value_mixed(self): + text = """\ + { + node1(arg: {a: [1, 2]}) + node2(arg: [1, {a: "a"}]) + } + """ + expected = [ + (0, Punctuation, "{"), + (1, Whitespace, "\n "), + (4, Name, "node1"), + (9, Punctuation, "("), + (10, Name, "arg"), + (13, Punctuation, ":"), + (14, Whitespace, " "), + (15, Punctuation, "{"), + (16, Name, "a"), + (17, Punctuation, ":"), + (18, Whitespace, " "), + (19, Punctuation, "["), + (20, Number.Integer, "1"), + (21, Punctuation, ","), + (22, Whitespace, " "), + (23, Number.Integer, "2"), + (24, Punctuation, "]"), + (25, Punctuation, "}"), + (26, Punctuation, ")"), + (27, Whitespace, "\n "), + (30, Name, "node2"), + (35, Punctuation, "("), + (36, Name, "arg"), + (39, Punctuation, ":"), + (40, Whitespace, " "), + (41, Punctuation, "["), + (42, Number.Integer, "1"), + (43, Punctuation, ","), + (44, Whitespace, " "), + (45, Punctuation, "{"), + (46, Name, "a"), + (47, Punctuation, ":"), + (48, Whitespace, " "), + (49, String, '"'), + (50, String, "a"), + (51, String, '"'), + (52, Punctuation, "}"), + (53, Punctuation, "]"), + (54, Punctuation, ")"), + (55, Whitespace, "\n"), + (56, Punctuation, "}"), + (57, Whitespace, "\n"), + ] + assert_tokens_equal(self, text, expected) -- cgit v1.2.1