# -*- coding: utf-8 -*- #
# frozen_string_literal: true
module Rouge
module Lexers
class GraphQL < RegexLexer
desc 'GraphQL'
tag 'graphql'
filenames '*.graphql', '*.gql'
mimetypes 'application/graphql'
name = /[_A-Za-z][_0-9A-Za-z]*/
state :root do
rule /\b(?:query|mutation|subscription)\b/, Keyword, :query_definition
rule /\{/ do
token Punctuation
push :query_definition
push :selection_set
end
rule /\bfragment\b/, Keyword, :fragment_definition
rule /\b(?:type|interface|enum)\b/, Keyword, :type_definition
rule /\b(?:input|schema)\b/, Keyword, :type_definition
rule /\bunion\b/, Keyword, :union_definition
mixin :basic
end
state :basic do
rule /\s+/m, Text::Whitespace
rule /#.*$/, Comment
rule /[!,]/, Punctuation
end
state :has_directives do
rule /(@#{name})(\s*)(\()/ do
groups Keyword, Text::Whitespace, Punctuation
push :arguments
end
rule /@#{name}\b/, Keyword
end
state :fragment_definition do
rule /\bon\b/, Keyword
mixin :query_definition
end
state :query_definition do
mixin :has_directives
rule /\b#{name}\b/, Name
rule /\(/, Punctuation, :variable_definitions
rule /\{/, Punctuation, :selection_set
mixin :basic
end
state :type_definition do
rule /\bimplements\b/, Keyword
rule /\b#{name}\b/, Name
rule /\(/, Punctuation, :variable_definitions
rule /\{/, Punctuation, :type_definition_set
mixin :basic
end
state :union_definition do
rule /\b#{name}\b/, Name
rule /\=/, Punctuation, :union_definition_variant
mixin :basic
end
state :union_definition_variant do
rule /\b#{name}\b/ do
token Name
pop!
push :union_definition_pipe
end
mixin :basic
end
state :union_definition_pipe do
rule /\|/ do
token Punctuation
pop!
push :union_definition_variant
end
rule /(?!\||\s+|#[^\n]*)/ do
pop! 2
end
mixin :basic
end
state :type_definition_set do
rule /\}/ do
token Punctuation
pop! 2
end
rule /\b(#{name})(\s*)(\()/ do
groups Name, Text::Whitespace, Punctuation
push :variable_definitions
end
rule /\b#{name}\b/, Name
rule /:/, Punctuation, :type_names
mixin :basic
end
state :arguments do
rule /\)/ do
token Punctuation
pop!
end
rule /\b#{name}\b/, Name
rule /:/, Punctuation, :value
mixin :basic
end
state :variable_definitions do
rule /\)/ do
token Punctuation
pop!
end
rule /\$#{name}\b/, Name::Variable
rule /\b#{name}\b/, Name
rule /:/, Punctuation, :type_names
rule /\=/, Punctuation, :value
mixin :basic
end
state :type_names do
rule /\b(?:Int|Float|String|Boolean|ID)\b/, Name::Builtin, :pop!
rule /\b#{name}\b/, Name, :pop!
rule /\[/, Punctuation, :type_name_list
mixin :basic
end
state :type_name_list do
rule /\b(?:Int|Float|String|Boolean|ID)\b/, Name::Builtin
rule /\b#{name}\b/, Name
rule /\]/ do
token Punctuation
pop! 2
end
mixin :basic
end
state :selection_set do
mixin :has_directives
rule /\}/ do
token Punctuation
pop!
pop! if state?(:query_definition) || state?(:fragment_definition)
end
rule /\b(#{name})(\s*)(\()/ do
groups Name, Text::Whitespace, Punctuation
push :arguments
end
rule /\b(#{name})(\s*)(:)/ do
groups Name, Text::Whitespace, Punctuation
end
rule /\b#{name}\b/, Name
rule /(\.\.\.)(\s+)(on)\b/ do
groups Punctuation, Text::Whitespace, Keyword
end
rule /\.\.\./, Punctuation
rule /\{/, Punctuation, :selection_set
mixin :basic
end
state :list do
rule /\]/ do
token Punctuation
pop!
pop! if state?(:value)
end
mixin :value
end
state :object do
rule /\}/ do
token Punctuation
pop!
pop! if state?(:value)
end
rule /\b(#{name})(\s*)(:)/ do
groups Name, Text::Whitespace, Punctuation
push :value
end
mixin :basic
end
state :value do
pop_unless_list = ->(t) {
->(m) {
token t
pop! unless state?(:list)
}
}
rule /\$#{name}\b/, &pop_unless_list[Name::Variable]
rule /\b(?:true|false|null)\b/, &pop_unless_list[Keyword::Constant]
rule /[+-]?[0-9]+\.[0-9]+(?:[eE][+-]?[0-9]+)?/, &pop_unless_list[Num::Float]
rule /[+-]?[1-9][0-9]*(?:[eE][+-]?[0-9]+)?/, &pop_unless_list[Num::Integer]
rule /"(\\[\\"]|[^"])*"/, &pop_unless_list[Str::Double]
rule /\b#{name}\b/, &pop_unless_list[Name]
rule /\{/, Punctuation, :object
rule /\[/, Punctuation, :list
mixin :basic
end
end
end
end