Files
vscode-codeql/extensions/ql-vscode/syntaxes/ql.tmLanguage.yml
2023-10-09 16:58:00 +01:00

1109 lines
30 KiB
YAML

---
# This file is transformed into the equivalent JSON TextMate grammar, with the following additional
# features available:
#
# The `regexOptions` Property
# A top-level property named `regexOptions` may be defined with a string value. This string
# represents the set of regular expression options to apply to all regular expressions throughout
# the file.
#
# Macros
# The `macros` element defines a map of macro names to replacement text. When a `match`, `begin`, or
# `end` property has a value that is a single-key map, the value is replaced with the value of the
# macro named by the key, with any use of `(?#)` in the macro text replaced with the text of the
# value of the key, surrounded by a non-capturing group (`(?:)`). For example:
#
# The `beginPattern` and `endPattern` Properties
# A rule can have a `beginPattern` or `endPattern` property whose value is a reference to another
# rule (e.g. `#other-rule`). The `beginPattern` property is replaced as follows:
#
# my-rule:
# beginPattern: '#other-rule'
#
# would be transformed to
#
# my-rule:
# begin: '(?#other-rule)'
# beginCaptures:
# '0':
# patterns:
# - include: '#other-rule'
#
# An `endPattern` property is transformed similary.
#
# macros:
# repeat: '(?#)*'
# repository:
# multi-letter:
# match:
# repeat: '[A-Za-z]'
# name: scope.multi-letter
#
# would be transformed to
#
# repository:
# multi-letter:
# match: '(?:[A-Za-z])*'
# name: scope.multi-letter
#
# Reference Expansion
# Any comment of the form `(?#ref-id)` in a `match`, `begin`, or `end` property will be replaced
# with the match text of the rule named "ref-id". If the rule named "ref-id" consists of just a
# `patterns` property with a list of `include` directives, the replacement pattern is the
# disjunction of the match patterns of all of the included rules.
name: QL
scopeName: source.ql
fileTypes: [ql, qll]
uuid: 7F6926BF-1C6C-468A-A7AA-215EBAC86A4E
regexOptions: 'x' # Ignore pattern whitespace
# Macros are parameterized patterns that can be used as a match elsewhere in the file.
# To use a macro, replace the string for a `match`, `begin`, or `end` property with a single-element
# map whose key is the name of the macro to invoke, and whose value is a string to be substituted for
# any usage of `(?#)` in the macro pattern definition.
macros:
keyword: '\b(?#)(?#end-of-id)'
patterns:
- include: '#module-member'
repository:
# A character that can appear somewhere in an identifier.
id-character:
match: '[0-9A-Za-z_]'
# Matches a position containing a non-identifier character. Used to ensure we do not match partial
# identifiers/keywords in other rules.
end-of-id:
match: '(?!(?#id-character))'
# A QL "simple identifier", which can begin with either an uppercase or a lowercase letter.
simple-id:
match: '\b [A-Za-z][0-9A-Za-z_]* (?#end-of-id)'
# An identifier beginning with a lowercase letter.
lower-id:
match: '\b [a-z][0-9A-Za-z_]* (?#end-of-id)'
# An identifier beginning with an uppercase letter.
upper-id:
match: '\b [A-Z][0-9A-Za-z_]* (?#end-of-id)'
# An identifier beginning with an `@` followed by a lowercase letter. Used to represent database
# types.
at-lower-id:
match: '@[a-z][0-9A-Za-z_]* (?#end-of-id)'
# A pattern that can start a comment.
comment-start:
match: '// | /\*'
# A pattern that can start a run of whitespace or a comment.
# Commonly used as a negative lookahead in the `end` regex of a nonterminal, when searching for
# tokens that can't start a child of that nonterminal.
whitespace-or-comment-start:
match: '\s | $ | (?#comment-start)'
# All tokens that can appear in any context.
non-context-sensitive:
patterns:
- include: '#comment'
- include: '#literal'
- include: '#operator-or-punctuation'
- include: '#keyword'
# Operators and punctuation
relational-operator:
match: '<=|<|>=|>'
name: keyword.operator.relational.ql
comparison-operator:
match: '=|\!\='
name: keyword.operator.comparison.ql
arithmetic-operator:
match: '\+|-|\*|/|%'
name: keyword.operator.arithmetic.ql
comma:
match: ','
name: punctuation.separator.comma.ql
semicolon:
match: ';'
name: punctuation.separator.statement.ql
dot:
match: '\.'
name: punctuation.accessor.ql
dotdot:
match: '\.\.'
name: punctuation.operator.range.ql
pipe:
match: '\|'
name: punctuation.separator.pipe.ql
open-paren:
match: '\('
name: punctuation.parenthesis.open.ql
close-paren:
match: '\)'
name: punctuation.parenthesis.close.ql
open-brace:
match: '\{'
name: punctuation.curlybrace.open.ql
close-brace:
match: '\}'
name: punctuation.curlybrace.close.ql
open-bracket:
match: '\['
name: punctuation.squarebracket.open.ql
close-bracket:
match: '\]'
name: punctuation.squarebracket.close.ql
open-angle:
match: '<'
name: punctuation.anglebracket.open.ql
close-angle:
match: '>'
name: punctuation.anglebracket.close.ql
operator-or-punctuation:
patterns:
- include: '#relational-operator'
- include: '#comparison-operator'
- include: '#arithmetic-operator'
- include: '#comma'
- include: '#semicolon'
- include: '#dot'
- include: '#dotdot'
- include: '#pipe'
- include: '#open-paren'
- include: '#close-paren'
- include: '#open-brace'
- include: '#close-brace'
- include: '#open-bracket'
- include: '#close-bracket'
- include: '#open-angle'
- include: '#close-angle'
# Keywords
dont-care:
match:
keyword: '_'
name: variable.language.dont-care.ql
and:
match:
keyword: 'and'
name: keyword.other.and.ql
any:
match:
keyword: 'any'
name: keyword.quantifier.any.ql
as:
match:
keyword: 'as'
name: keyword.other.as.ql
asc:
match:
keyword: 'asc'
name: keyword.order.asc.ql
avg:
match:
keyword: 'avg'
name: keyword.aggregate.avg.ql
boolean:
match:
keyword: 'boolean'
name: keyword.type.boolean.ql
by:
match:
keyword: 'by'
name: keyword.order.by.ql
class:
match:
keyword: 'class'
name: keyword.other.class.ql
concat:
match:
keyword: 'concat'
name: keyword.aggregate.concat.ql
count:
match:
keyword: 'count'
name: keyword.aggregate.count.ql
date:
match:
keyword: 'date'
name: keyword.type.date.ql
desc:
match:
keyword: 'desc'
name: keyword.order.desc.ql
else:
match:
keyword: 'else'
name: keyword.other.else.ql
exists:
match:
keyword: 'exists'
name: keyword.quantifier.exists.ql
extends:
match:
keyword: 'extends'
name: keyword.other.extends.ql
false:
match:
keyword: 'false'
name: constant.language.boolean.false.ql
float:
match:
keyword: 'float'
name: keyword.type.float.ql
forall:
match:
keyword: 'forall'
name: keyword.quantifier.forall.ql
forex:
match:
keyword: 'forex'
name: keyword.quantifier.forex.ql
from:
match:
keyword: 'from'
name: keyword.other.from.ql
if:
match:
keyword: 'if'
name: keyword.other.if.ql
implies:
match:
keyword: 'implies'
name: keyword.other.implies.ql
import:
match:
keyword: 'import'
name: keyword.other.import.ql
in:
match:
keyword: 'in'
name: keyword.other.in.ql
instanceof:
match:
keyword: 'instanceof'
name: keyword.other.instanceof.ql
int:
match:
keyword: 'int'
name: keyword.type.int.ql
max:
match:
keyword: 'max'
name: keyword.aggregate.max.ql
min:
match:
keyword: 'min'
name: keyword.aggregate.min.ql
module:
match:
keyword: 'module'
name: keyword.other.module.ql
newtype:
match:
keyword: 'newtype'
name: keyword.other.newtype.ql
none:
match:
keyword: 'none'
name: keyword.quantifier.none.ql
not:
match:
keyword: 'not'
name: keyword.other.not.ql
or:
match:
keyword: 'or'
name: keyword.other.or.ql
order:
match:
keyword: 'order'
name: keyword.order.order.ql
predicate:
match:
keyword: 'predicate'
name: keyword.other.predicate.ql
rank:
match:
keyword: 'rank'
name: keyword.aggregate.rank.ql
result:
match:
keyword: 'result'
name: variable.language.result.ql
select:
match:
keyword: 'select'
name: keyword.query.select.ql
strictconcat:
match:
keyword: 'strictconcat'
name: keyword.aggregate.strictconcat.ql
strictcount:
match:
keyword: 'strictcount'
name: keyword.aggregate.strictcount.ql
strictsum:
match:
keyword: 'strictsum'
name: keyword.aggregate.strictsum.ql
string:
match:
keyword: 'string'
name: keyword.type.string.ql
sum:
match:
keyword: 'sum'
name: keyword.aggregate.sum.ql
super:
match:
keyword: 'super'
name: variable.language.super.ql
then:
match:
keyword: 'then'
name: keyword.other.then.ql
this:
match:
keyword: 'this'
name: variable.language.this.ql
true:
match:
keyword: 'true'
name: constant.language.boolean.true.ql
unique:
match:
keyword: 'unique'
name: keyword.aggregate.unique.ql
where:
match:
keyword: 'where'
name: keyword.query.where.ql
# Any "true" keyword (not including annotations).
keyword:
patterns:
- include: '#dont-care'
- include: '#and'
- include: '#any'
- include: '#as'
- include: '#asc'
- include: '#avg'
- include: '#boolean'
- include: '#by'
- include: '#class'
- include: '#concat'
- include: '#count'
- include: '#date'
- include: '#desc'
- include: '#else'
- include: '#exists'
- include: '#extends'
- include: '#false'
- include: '#float'
- include: '#forall'
- include: '#forex'
- include: '#from'
- include: '#if'
- include: '#implies'
- include: '#import'
- include: '#in'
- include: '#instanceof'
- include: '#int'
- include: '#max'
- include: '#min'
- include: '#module'
- include: '#newtype'
- include: '#none'
- include: '#not'
- include: '#or'
- include: '#order'
- include: '#predicate'
- include: '#rank'
- include: '#result'
- include: '#select'
- include: '#strictconcat'
- include: '#strictcount'
- include: '#strictsum'
- include: '#string'
- include: '#sum'
- include: '#super'
- include: '#then'
- include: '#this'
- include: '#true'
# `unique` is not really a keyword, but we'll highlight it as if it is.
- include: '#unique'
- include: '#where'
# A keyword that can be the first token of a predicate declaration.
predicate-start-keyword:
patterns:
- include: '#boolean'
- include: '#date'
- include: '#float'
- include: '#int'
- include: '#predicate'
- include: '#string'
# Annotation keywords
abstract:
match:
keyword: 'abstract'
name: storage.modifier.abstract.ql
additional:
match:
keyword: 'additional'
name: storage.modifier.additional.ql
bindingset:
match:
keyword: 'bindingset'
name: storage.modifier.bindingset.ql
cached:
match:
keyword: 'cached'
name: storage.modifier.cached.ql
default:
match:
keyword: 'default'
name: storage.modifier.default.ql
deprecated:
match:
keyword: 'deprecated'
name: storage.modifier.deprecated.ql
external:
match:
keyword: 'external'
name: storage.modifier.external.ql
final:
match:
keyword: 'final'
name: storage.modifier.final.ql
language:
match:
keyword: 'language'
name: storage.modifier.language.ql
library:
match:
keyword: 'library'
name: storage.modifier.library.ql
override:
match:
keyword: 'override'
name: storage.modifier.override.ql
pragma:
match:
keyword: 'pragma'
name: storage.modifier.pragma.ql
private:
match:
keyword: 'private'
name: storage.modifier.private.ql
query:
match:
keyword: 'query'
name: storage.modifier.query.ql
signature:
match:
keyword: 'signature'
name: storage.modifier.signature.ql
transient:
match:
keyword: 'transient'
name: storage.modifier.transient.ql
annotation-keyword:
patterns:
- include: '#abstract'
- include: '#additional'
- include: '#bindingset'
- include: '#cached'
- include: '#default'
- include: '#deprecated'
- include: '#external'
- include: '#final'
- include: '#language'
- include: '#library'
- include: '#override'
- include: '#pragma'
- include: '#private'
- include: '#query'
- include: '#signature'
- include: '#transient'
implements:
match:
keyword: 'implements'
name: keyword.other.implements.ql
# A QL comment, regardless of form.
comment:
patterns:
# A QLDoc (`/** */`) comment.
- begin: '/\*\*'
end: '\*/'
name: comment.block.documentation.ql
# Highlight tag names within the QLDoc.
patterns:
- begin: '(?<=/\*\*)([^*]|\*(?!/))*$'
while: '(^|\G)\s*([^*]|\*(?!/))(?=([^*]|[*](?!/))*$)'
patterns:
- include: 'text.html.markdown#fenced_code_block'
- include: 'text.html.markdown#lists'
- include: 'text.html.markdown#inline'
- match: '\G\s* (@\S+)'
name: keyword.tag.ql
# A block (`/* */`) comment.
- begin: '/\*'
end: '\*/'
name: comment.block.ql
# A single line (`//`) comment.
- match: //.*$
name: comment.line.double-slash.ql
# List of rules that can be matched within the body of a module, including at file scope.
module-member:
patterns:
- include: '#import-directive'
- include: '#import-as-clause'
- include: '#module-declaration'
- include: '#newtype-declaration'
# See the comment on newtype-declaration for why we include these next three nonterminals at the
# module-member level instead of as part of the newtype-declaration.
- include: '#newtype-branch-name-with-prefix'
- include: '#predicate-parameter-list'
- include: '#predicate-body'
- include: '#class-declaration'
- include: '#select-clause'
- include: '#predicate-or-field-declaration'
- include: '#non-context-sensitive'
- include: '#annotation'
# The argument list of an instantiation, enclosed in angle brackets.
instantiation-args:
beginPattern: '#open-angle'
endPattern: '#close-angle'
name: meta.type.parameters.ql
patterns:
# Include `#instantiation-args` first so that `#open-angle` and `#close-angle` take precedence
# over `#relational-operator`.
- include: '#instantiation-args'
- include: '#non-context-sensitive'
- match: '(?#simple-id)'
name: entity.name.type.namespace.ql
# An `import` directive. Note that we parse the optional `as` clause as a separate top-level
# directive, because otherwise it's too hard to figure out where the `import` directive ends.
import-directive:
beginPattern: '#import'
# TextMate makes it tricky to tell whether an identifier that we encounter is part of the
# `import` directive or whether it's the first token of the next module-level declaration.
# To find the end of the import directive, we'll look for a zero-width match where the previous
# token is either an identifier (other than `import`) or a `>`, and the next token is not a `.`,
# `<`, `,`, or `::`. This works for nearly all real-world `import` directives, but it will end the
# `import` directive too early if there is a comment or line break between two components of the
# module expression.
end: '(?<!\bimport)(?<=(?:\>)|[A-Za-z0-9_]) (?!\s*(\.|\:\:|\,|(?#open-angle)))'
name: meta.block.import-directive.ql
patterns:
# Include `#instantiation-args` first so that `#open-angle` and `#close-angle` take precedence
# over `#relational-operator`.
- include: '#instantiation-args'
- include: '#non-context-sensitive'
- match: '(?#simple-id)'
name: entity.name.type.namespace.ql
# The end pattern for an `as` clause, whether on an `import` directive, in an aggregate, or on a
# `select`.
end-of-as-clause:
match: '(?: (?<=(?#id-character)) (?!(?#id-character)) (?<!(?<!(?#id-character))as)) |
(?=\s* (?!(?#comment-start) | (?#simple-id)) \S) |
(?=\s* (?#keyword))'
# The `as` clause of an `import` directive. We treat this as a separate module-scope directive.
import-as-clause:
# Start at the `as` keyword.
beginPattern: '#as'
# The end pattern is tricky.
# We want to allow an aribtrary amount of whitespace and comments, followed by a single
# simple-id. To do this, we end when one of the following matches:
# - We are at the end of an identifier, and that identifier is not `as`.
# - The next non-whitespace character is not the start of a comment or a simple-id.
# - The next non-whitespace character is a keyword.
end: '(?#end-of-as-clause)'
name: meta.block.import-as-clause.ql
patterns:
- include: '#non-context-sensitive'
- match: '(?#simple-id)'
name: entity.name.type.namespace.ql
# The `implements` clause of a module definition.
implements-clause:
beginPattern: '#implements'
# Ends at the first `{`.
# REVIEW: This could be any token not allowed in a type.
end: '(?= \{ )'
name: meta.block.implements-clause.ql
patterns:
- include: '#non-context-sensitive'
- match: '(?#simple-id)|(?#at-lower-id)'
name: entity.name.type.ql
# A `module` declaration, whether a module definition or an alias declaration.
module-declaration:
# Starts with the `module` keyword.
beginPattern: '#module'
# Ends after a `}` or `;`
end: '(?<=\}|;)'
name: meta.block.module-declaration.ql
patterns:
- include: '#module-body'
- include: '#implements-clause'
- include: '#non-context-sensitive'
# Any simple-id within a module head is part of a module name.
- match: '(?#simple-id)'
name: entity.name.type.namespace.ql
# The body of a `module` definition.
module-body:
beginPattern: '#open-brace'
endPattern: '#close-brace'
name: meta.block.module-body.ql
patterns:
- include: '#module-member'
# A simple-id followed by a `::` is a module qualifier.
module-qualifier:
match: '(?#simple-id) (?=\s*\:\:)'
name: entity.name.type.namespace.ql
# The declaration of a predicate or field.
predicate-or-field-declaration:
# Starts with one of the following:
# - A simpleId that is neither a keyword nor an annotation keyword.
# - A keyword that can start a predicate (e.g. a primitive type, or `predicate`).
# = An atLowerId.
begin: '(?:(?=(?#simple-id))(?!(?#keyword)|(?#annotation-keyword))) |
(?=(?#predicate-start-keyword)) |
(?=(?#at-lower-id))'
# Ends after a `}` or `;`
end: '(?<=\}|;)'
name: meta.block.predicate-or-field-declaration.ql
patterns:
- include: '#predicate-parameter-list'
- include: '#predicate-body'
- include: '#non-context-sensitive'
# An identifier followed by a `::` is a module.
- include: '#module-qualifier'
# A lower-id followed by a `;` is a field name.
- match: '(?#lower-id)(?=\s*;)'
name: variable.field.ql
# Any other lower-id is assumed to be a predicate name.
- match: '(?#lower-id)'
name: entity.name.function.ql
# Any other upper-id or at-lower-id is a type name.
- match: '(?#upper-id)|(?#at-lower-id)'
name: entity.name.type.ql
# The parenthesized parameter list of a predicate.
predicate-parameter-list:
beginPattern: '#open-paren'
endPattern: '#close-paren'
name: meta.block.predicate-parameter-list.ql
# Figuring out which kind of identifier we're looking at is tricky in a parameter declaration,
# because variable names can be upper-ids or lower-ids. We'll use some heuristics.
patterns:
- include: '#non-context-sensitive'
# An upper-id followed by a `,` or a `)` is assumed to be a variable name.
- match: '(?#upper-id)(?=\s*(?:,|\)))'
name: variable.parameter.ql
# A simple-id followed by a `::` is a module qualifier.
- include: '#module-qualifier'
# Any other upper-id is assumed to be part of a type name.
- match: '(?#upper-id)|(?#at-lower-id)'
name: entity.name.type.ql
# Any other lower-id is assumed to be a variable name.
- match: '(?#lower-id)'
name: variable.parameter.ql
# An `as` clause in an aggregate or `select`.
expr-as-clause:
# Start at the `as` keyword.
beginPattern: '#as'
end: '(?#end-of-as-clause)'
name: meta.block.expr-as-clause.ql
patterns:
- include: '#non-context-sensitive'
- match: '(?#simple-id)'
name: variable.other.ql
# The contents of a predicate body.
predicate-body-contents:
patterns:
- include: '#expr-as-clause'
- include: '#non-context-sensitive'
# A simple-id followed by a `::` is a module qualifier.
- include: '#module-qualifier'
# Any lower-id followed by a `(`, optionally with a `*` or `+` in between,
# is a predicate name.
- match: '(?#lower-id)\s*(?:\*|\+)?\s*(?=\()'
name: entity.name.function.ql
# Any other lower-id is assumed to be a variable name.
- match: '(?#lower-id)'
name: variable.other.ql
# Any other upper-id is assumed to be part of a type name.
- match: '(?#upper-id)|(?#at-lower-id)'
name: entity.name.type.ql
# The brace-enclosed body of a predicate definition.
predicate-body:
beginPattern: '#open-brace'
endPattern: '#close-brace'
name: meta.block.predicate-body.ql
patterns:
- include: '#predicate-body-contents'
# An annotation on a declaration.
annotation:
patterns:
- include: '#bindingset-annotation'
- include: '#language-annotation'
- include: '#pragma-annotation'
- include: '#annotation-keyword'
# A `pragma` annotation, including its arguments.
bindingset-annotation:
beginPattern: '#bindingset'
# Ends after the next `]`, or when we encounter something other than a `[`.
end: '(?! (?#whitespace-or-comment-start) | \[ ) |
(?<=\])'
name: meta.block.bindingset-annotation.ql
patterns:
- include: '#bindingset-annotation-body'
- include: '#non-context-sensitive'
# The bracket-enclosed body of a `bindingset` annotation.
bindingset-annotation-body:
beginPattern: '#open-bracket'
endPattern: '#close-bracket'
name: meta.block.bindingset-annotation-body.ql
patterns:
- include: '#non-context-sensitive'
- match: '(?#simple-id)'
name: variable.parameter.ql
# A `language` annotation, including its arguments.
language-annotation:
beginPattern: '#language'
# Ends after the next `]`, or when we encounter something other than a `[`.
end: '(?! (?#whitespace-or-comment-start) | \[ ) |
(?<=\])'
name: meta.block.language-annotation.ql
patterns:
- include: '#language-annotation-body'
- include: '#non-context-sensitive'
# The bracket-enclosed body of a `language` annotation.
language-annotation-body:
beginPattern: '#open-bracket'
endPattern: '#close-bracket'
name: meta.block.language-annotation-body.ql
patterns:
- include: '#non-context-sensitive'
- match:
keyword: 'monotonicAggregates'
name: storage.modifier.ql
# A `pragma` annotation, including its arguments.
pragma-annotation:
beginPattern: '#pragma'
# Ends after the next `]`, or when we encounter something other than a `[`.
end: '(?! (?#whitespace-or-comment-start) | \[ ) |
(?<=\])'
name: meta.block.pragma-annotation.ql
patterns:
- include: '#pragma-annotation-body'
- include: '#non-context-sensitive'
# The bracket-enclosed body of a `pragma` annotation.
pragma-annotation-body:
beginPattern: '#open-bracket'
endPattern: '#close-bracket'
name: meta.block.pragma-annotation-body.ql
patterns:
- match: '\b(?:inline|noinline|nomagic|noopt)\b'
name: storage.modifier.ql
# The declaration of an IPA type.
# This only includes the `newtype` keyword and the identifier for the IPA type itself. The
# branches of the IPA type are modeled as separate nonterminals contained directly in the module
# body. This is kind of hacky, but without it, we don't seem to have a way to get TextMate to
# handle this:
# ```ql
# newtype TRoot =
# TBranch1(int x) {
# x = 5
# } or
# TBranch2() // No body
#
# TOther getOther() { any() }
# ```
# If the branches are within the newtype declaration node, it's very hard to get the upper-id for
# the name of the IPA type to be included in the newtype declaration node, without also including
# the `TOther` upper-id in the declaration node.
newtype-declaration:
beginPattern: '#newtype'
# We're expecting a newtype-declaration-without-keyword immediately after the `newtype` keyword,
# so end if we see anything other than the upper-id that starts it, or whitespace, or a comment.
# An upper-id can't start anything else at module scope, so once we see the rest of this
# newtype-declaration, whatever comes next should end this block.
end: '(?#upper-id)'
endCaptures:
'0':
name: entity.name.type.ql
name: meta.block.newtype-declaration.ql
patterns:
- include: '#non-context-sensitive'
# A branch of an IPA type, including just the `=` or `or` prefix and the name of the branch.
# The parameter list and body are separate nonterminals contained directly within the module body.
# See the comment for newtype-declaration for why.
newtype-branch-name-with-prefix:
begin: '\= | (?#or)'
beginCaptures:
'0':
patterns:
- include: '#or'
- include: '#comparison-operator'
end: '(?#upper-id)'
endCaptures:
'0':
name: entity.name.type.ql
name: meta.block.newtype-branch-name-with-prefix.ql
patterns:
- include: '#non-context-sensitive'
# The declaration of a class, include an alias.
class-declaration:
beginPattern: '#class'
# Ends after a `}` or `;`.
end: '(?<= \} | ; )'
name: meta.block.class-declaration.ql
patterns:
- include: '#class-body'
- include: '#extends-clause'
- include: '#non-context-sensitive'
- match: '(?#upper-id)'
name: entity.name.type.class.ql
# The `extends` clause of a class definition.
extends-clause:
beginPattern: '#extends'
# Ends at the first `{`.
# REVIEW: This could be any token not allowed in a type.
end: '(?= \{ )'
name: meta.block.extends-clause.ql
patterns:
- include: '#non-context-sensitive'
- match: '(?#simple-id)|(?#at-lower-id)'
name: entity.name.type.ql
# The body of a class definition.
class-body:
beginPattern: '#open-brace'
endPattern: '#close-brace'
name: meta.block.class-body.ql
patterns:
- include: '#class-member'
# A declaration within a class definition.
class-member:
patterns:
- include: '#predicate-or-field-declaration'
- include: '#annotation'
- include: '#non-context-sensitive'
# An entire `select` clause, including the `from`, `where`, and `select` sections.
select-clause:
# Starts just before the `from`, `where`, or `select` keyword.
begin: '(?=(?#from)|(?#where)|(?#select))'
# Ends when we encounter anything other than `from`, `where`, or `select`.
end: '(?!(?#from)|(?#where)|(?#select))'
name: meta.block.select-clause.ql
patterns:
- include: '#from-section'
- include: '#where-section'
- include: '#select-section'
# The `from` section of a `select` clause.
from-section:
# Starts just before the `from` keyword.
beginPattern: '#from'
# Ends at the subsequent `select` or `where` keyword.
end: '(?= (?#select) | (?#where) )'
name: meta.block.from-section.ql
# Figuring out which kind of identifier we're looking at is tricky in a `from` section,
# because variable names can be upper-ids or lower-ids. We'll use some heuristics.
patterns:
- include: '#non-context-sensitive'
# An upper-id followed by a `,`, `where`, `select`, or newline is assumed to be a variable
# name.
- match: '(?#upper-id)(?=\s*(?:,|(?#where)|(?#select)|$))'
name: variable.parameter.ql
# A simple-id followed by a `::` is a module qualifier.
- include: '#module-qualifier'
# Any other upper-id is assumed to be part of a type name.
- match: '(?#upper-id)|(?#at-lower-id)'
name: entity.name.type.ql
# Any other lower-id is assumed to be a variable name.
- match: '(?#lower-id)'
name: variable.parameter.ql
# The `where` section of a `select` clause.
where-section:
beginPattern: '#where'
end: '(?=(?#select))'
name: meta.block.where-section.ql
patterns:
- include: '#predicate-body-contents'
# The `select` section of a `select` clause.
select-section:
beginPattern: '#select'
# REVIEW: It's hard to figure out where the `select` section ends, but we should be able to do
# bettern than this.
end: '(?=\n)'
name: meta.block.select-section.ql
patterns:
- include: '#predicate-body-contents'
- include: '#select-as-clause'
# The `as` clause within a `select` expression.
select-as-clause:
beginPattern: '#as'
# Ends after the first identifier we encounter.
# REVIEW: Make similar to import-as-clause.
end: '(?<=(?#id-character)(?#end-of-id))'
match: meta.block.select-as-clause.ql
patterns:
- include: '#non-context-sensitive'
- match: '(?#simple-id)'
name: variable.other.ql
# A literal (not including Boolean literals, which are keywords).
literal:
patterns:
- include: '#float-literal'
- include: '#int-literal'
- include: '#string-literal'
# An integer literal.
int-literal:
match: '-?[0-9]+(?![0-9])'
name: constant.numeric.decimal.ql
# A float literal.
float-literal:
match: '-?[0-9]+\.[0-9]+(?![0-9])'
name: constant.numeric.decimal.ql
# A string literal.
string-literal:
name: string.quoted.double.ql
begin: '"'
beginCaptures:
'0': { name: punctuation.definition.string.begin.ql }
end: '(") | ((?:[^\\\n])$)'
endCaptures:
'1': { name: punctuation.definition.string.end.ql }
'2': { name: invalid.illegal.newline.ql }
patterns:
- include: '#string-escape'
# An escape sequence in a string.
string-escape:
match: '\\["\\nrt]'
name: constant.character.escape.ql