sublime/Python/Python.sublime-syntax
Ethan Dalool e358a26480
Make print purple instead of blue by making it a keyword again.
I've been looking at purple print statements for seven years now so
the shift to builtin-blue was jarring to me. Also, this syntax made
print look purple before putting the parens on, then blue after,
which made it even more unpleasant.
2021-08-24 23:40:05 -07:00

2271 lines
80 KiB
YAML

%YAML 1.2
---
name: Python
file_extensions:
- py
- py3
- pyw
- pyi
- pyx
- pyx.in
- pxd
- pxd.in
- pxi
- pxi.in
- rpy
- cpy
- SConstruct
- Sconstruct
- sconstruct
- SConscript
- gyp
- gypi
- Snakefile
- vpy
- wscript
- bazel
- bzl
first_line_match: ^#!\s*/.*\bpython(\d(\.\d)?)?\b
scope: source.python
variables:
# We support unicode here because Python 3 is the future
identifier_continue: '[[:alnum:]_]'
identifier: '\b[[:alpha:]_]{{identifier_continue}}*\b'
identifier_constant: '\b(?:[\p{Lu}_][\p{Lu}_\d]*)?[\p{Lu}]{2,}[\p{Lu}_\d]*\b' # require 2 consecutive upper-case letters
digits: (?:\d+(?:_\d+)*)
exponent: (?:[eE][-+]?{{digits}})
path: '({{identifier}}[ ]*\.[ ]*)*{{identifier}}'
sql_indicator: \s*(?:SELECT|INSERT|UPDATE|DELETE|CREATE|REPLACE|ALTER|WITH)\b
illegal_names: (?:and|as|assert|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|not|or|pass|raise|return|try|while|with|yield)
format_spec: |-
(?x:
(?:.? [<>=^])? # fill align
[ +-]? # sign
\#? # alternate form
# technically, octal and hexadecimal integers are also supported as 'width', but rarely used
\d* # width
,? # thousands separator
(?:\.\d+)? # precision
[bcdeEfFgGnosxX%]? # type
)
strftime_spec: '(?:%(?:[aAwdbBGmyYHIpMSfzZjuUVWcxX%]|-[dmHIMSj]))'
# This can be used in look-aheads to parse simple expressions.
# Can't be recursive, because sregex doesn't support that,
# so we're skipping parentheses.
# Can't parse multiple lines as well, for obvious reasons
simple_expression: |-
(?x:
\s+ # whitespace
| [urfb]*"(?:\\.|[^"])*" # strings
| [urfb]*'(?:\\.|[^'])*' # ^
| [\d.ej]+ # numerics
| [+*/%@-] | // | and | or # operators
| {{path}} # a path
)*
contexts:
main:
- include: statements
statements:
- include: docstrings
- include: line-statements
- include: block-statements
- include: classes
- include: functions
- include: modifiers
- include: assignments
- match: ;
scope: punctuation.terminator.statement.python
- include: expression-as-a-statement
line-statements:
- include: imports
- include: decorators
- match: \b(raise)\b
scope: keyword.control.flow.raise.python
push:
- meta_scope: meta.statement.raise.python
- include: line-continuation-or-pop
- match: \b(from)\b
scope: keyword.control.flow.raise.from.python
set:
- meta_scope: meta.statement.raise.python
- include: line-continuation-or-pop
- include: expression-in-a-statement
- include: expression-in-a-statement
- match: \b(assert)\b
scope: keyword.control.flow.assert.python
- match: \b(del)\b
scope: keyword.other.del.python
- match: \b(print)\b(?! *([,.\]}]))
scope: keyword.other.print.python
- match: \b(exec)\b(?! *($|[,.()\]}]))
scope: keyword.other.exec.python
- match: \b(return)\b
scope: keyword.control.flow.return.python
- match: \b(break)\b
scope: keyword.control.flow.break.python
- match: \b(continue)\b
scope: keyword.control.flow.continue.python
- match: \b(pass)\b
scope: keyword.control.flow.pass.python
imports:
- match: \b(import)\b
scope: keyword.control.import.python
push:
- imports-import-body
- expect-absolute-import
- match: \b(from)\b
scope: keyword.control.import.from.python
push:
- imports-from-body
- maybe-relative-import
imports-import-body:
- meta_scope: meta.statement.import.python
- include: line-continuation-or-pop
- match: ','
scope: punctuation.separator.import-list.python
push: expect-absolute-import
- match: (?=\bas\b)
set: import-alias-list
- include: qualified-name
- match: (?=\S)
pop: true
imports-from-body:
- meta_scope: meta.statement.import.python
- meta_content_scope: meta.import-source.python
- include: line-continuation-or-pop
- match: (?=\bas\b)
set: import-alias-list
- match: (?=\bimport\b)
set:
- meta_include_prototype: false
- match: import
scope: keyword.control.import.python
set: imports-from-import-body
- match: '{{illegal_names}}\b'
scope: meta.import-path.python invalid.illegal.name.python
- match: '{{identifier}}'
scope: meta.import-path.python meta.import-name.python
- match: \s*(\.) *(?={{identifier}}|$)
captures:
0: meta.import-path.python
1: punctuation.accessor.dot.python
- match: \s*(\. *\S+) # matches and consumes the remainder of "abc.123" or "abc.+"
captures:
0: meta.import-path.python
1: invalid.illegal.name.python
- match: (?=\S)
pop: true
imports-from-import-body:
- meta_scope: meta.statement.import.python
- include: line-continuation-or-pop
- match: (?=\()
set:
- meta_include_prototype: false
- match: \(
scope: punctuation.section.import-list.begin.python
set:
- meta_scope: meta.statement.import.python meta.import-list.python
- match: \)
scope: punctuation.section.import-list.end.python
pop: true
- include: comments
- include: import-name-list
- match: (?=\S)
set: import-alias-list
import-alias-list:
- meta_content_scope: meta.statement.import.python
- include: line-continuation-or-pop
- include: import-name-list
import-name-list:
- match: ','
scope: punctuation.separator.import-list.python
- match: \*
scope: constant.language.import-all.python
- match: \b(as)\b
scope: keyword.control.import.as.python
- include: name
- match: '[^\s,)]+'
scope: invalid.illegal.name.import.python
expect-absolute-import:
- include: line-continuation-or-pop
- match: \.+
scope: invalid.illegal.unexpected-relative-import.python
- match: (?=\S)
pop: true
maybe-relative-import:
- include: line-continuation-or-pop
- match: \.+
scope: meta.import-path.python keyword.control.import.relative.python
- match: (?=\S)
pop: true
block-statements:
# async for ... in ...:
- match: \b(async +)?(for)\b
captures:
1: storage.modifier.async.python
2: keyword.control.loop.for.python
push:
- meta_scope: meta.statement.loop.for.python
- include: line-continuation-or-pop
- match: \bin\b
scope: keyword.control.loop.for.in.python
set:
- meta_content_scope: meta.statement.loop.for.python
- include: line-continuation-or-pop
- match: ':(?!=)'
scope: meta.statement.loop.for.python punctuation.section.block.loop.for.python
pop: true
- include: expression-in-a-statement
- match: ':(?!=)'
scope: invalid.illegal.missing-in.python
pop: true
- include: target-list
# async with ... as ...:
- match: \b(async +)?(with)\b
captures:
1: storage.modifier.async.python
2: keyword.control.flow.with.python
push: with-body
# except ... as ...:
- match: \bexcept\b
scope: keyword.control.exception.catch.python
push:
- meta_scope: meta.statement.exception.catch.python
- include: line-continuation-or-pop
- match: ':(?!=)'
scope: punctuation.section.block.exception.catch.python
pop: true
- match: '\bas\b'
scope: keyword.control.exception.catch.as.python
set:
- meta_content_scope: meta.statement.exception.catch.python
- include: line-continuation-or-pop
- match: ':'
scope: meta.statement.exception.catch.python punctuation.section.block.exception.catch.python
pop: true
- include: name
- include: target-list
- match: \bif\b
scope: keyword.control.conditional.if.python
push:
- meta_scope: meta.statement.conditional.if.python
- include: line-continuation-or-pop
- match: ':(?!=)'
scope: punctuation.section.block.conditional.if.python
pop: true
- include: expression-in-a-statement
- match: \bwhile\b
scope: keyword.control.loop.while.python
push:
- meta_scope: meta.statement.loop.while.python
- include: line-continuation-or-pop
- match: ':(?!=)'
scope: punctuation.section.block.loop.while.python
pop: true
- include: expression-in-a-statement
- match: \b(else)\b(?:\s*(:))?
scope: meta.statement.conditional.else.python
captures:
1: keyword.control.conditional.else.python
2: punctuation.section.block.conditional.else.python
- match: \b(try)\b(?:\s*(:))?
scope: meta.statement.exception.try.python
captures:
1: keyword.control.exception.try.python
2: punctuation.section.block.exception.try.python
- match: \b(finally)\b(?:\s*(:))?
scope: meta.statement.exception.finally.python
captures:
1: keyword.control.exception.finally.python
2: punctuation.section.block.exception.finally.python
- match: \belif\b
scope: keyword.control.conditional.elseif.python
push:
- meta_scope: meta.statement.conditional.elseif.python
- match: ':(?!=)'
scope: punctuation.section.block.conditional.elseif.python
pop: true
- match: $\n?
pop: true
- include: expression-in-a-statement
with-body:
- meta_scope: meta.statement.with.python
- include: line-continuation-or-pop
- match: \b(as)\b
scope: keyword.control.flow.with.as.python
set: with-as
- match: ':(?!=)'
scope: punctuation.section.block.with.python
pop: true
- match: ','
scope: punctuation.separator.with-resources.python
- include: expression-in-a-statement
with-as:
- meta_scope: meta.statement.with.python
- include: line-continuation-or-pop
- match: ':'
scope: punctuation.section.block.with.python
pop: true
- match: ','
scope: punctuation.separator.with-resources.python
set: with-body
- include: name
- include: groups
- include: lists
expressions-common:
- include: comments
- include: constants
- include: numbers
- include: yields
- include: operators
- include: lambda
- match: \b(await)\b
scope: keyword.other.await.python
- include: inline-if
- include: strings
- include: function-calls
- include: item-access
- include: lists
- include: dictionaries-and-sets
- include: tuples
- include: groups
- match: \)
scope: invalid.illegal.stray.brace.round.python
- match: \]
scope: invalid.illegal.stray.brace.square.python
- match: \}
scope: invalid.illegal.stray.brace.curly.python
- include: line-continuation
# Always include these last and only one at a time!
expression-as-a-statement:
- include: expressions-common
- include: qualified-name
expression-in-a-statement:
# Differs from expression-as-a-statement in that:
# - invalid-name matches will pop the current context
# - assignment expressions
- include: expressions-common
- include: illegal-names-pop
- include: qualified-name
- include: assignment-expression
expression-in-a-group: # Always include this last!
# Differs from expression-in-a-statement in that:
# - accessor matching continues into the next line
- include: expression-in-a-statement
- match: '(\.) *(?={{identifier}})'
captures:
1: punctuation.accessor.dot.python
push:
- include: magic-function-names
- include: magic-variable-names
- include: illegal-names
- include: generic-names
- match: ''
pop: true
after-expression:
# direct function call
- match: '\s*(\()'
captures:
1: punctuation.section.arguments.begin.python
push: [function-call-arguments, allow-unpack-operators]
# item access
- match: '\s*(\[)'
captures:
1: meta.item-access.python punctuation.section.brackets.begin.python
push:
- meta_content_scope: meta.item-access.arguments.python
- match: \]
scope: meta.item-access.python punctuation.section.brackets.end.python
pop: true
- include: illegal-assignment-expression
- match: ':'
scope: punctuation.separator.slice.python
- include: expression-in-a-group
# indirect function call following attribute access
- include: function-calls
# arbitrary attribute access
- match: '\s*(\.)'
captures:
1: punctuation.accessor.dot.python
push:
- include: magic-function-names
- include: magic-variable-names
- include: illegal-names
- include: generic-names
- match: ''
pop: true
- match: ''
pop: true
comments:
- match: "#"
scope: punctuation.definition.comment.python
push:
- meta_scope: comment.line.number-sign.python
- match: \n
pop: true
constants:
- match: \b(None|True|False|Ellipsis|NotImplemented|__debug__)\b
scope: constant.language.python
- match: \.{3}(?!\w)
scope: constant.language.python
numbers:
# https://docs.python.org/3/reference/lexical_analysis.html#numeric-literals
# hexadecimal
- match: \b(0[xX])(\h*)([lL]) # py2
scope: meta.number.integer.hexadecimal.python
captures:
1: constant.numeric.base.python
2: constant.numeric.value.python
3: constant.numeric.suffix.python
- match: \b(0[xX])((?:_?\h)+)
scope: meta.number.integer.hexadecimal.python
captures:
1: constant.numeric.base.python
2: constant.numeric.value.python
# octal
- match: \b(0[oO]?)((?=[oO]|[0-7])[0-7]*)([lL]) # py2
scope: meta.number.integer.octal.python
captures:
1: constant.numeric.base.python
2: constant.numeric.value.python
3: constant.numeric.suffix.python
- match: \b(0)([0-7]+) # py2
scope: meta.number.integer.octal.python
captures:
1: constant.numeric.base.python
2: constant.numeric.value.python
- match: \b(0[oO])((?:_?[0-7])+)
scope: meta.number.integer.octal.python
captures:
1: constant.numeric.base.python
2: constant.numeric.value.python
# binary
- match: \b(0[bB])([01]*)([lL]) # py2
scope: meta.number.integer.binary.python
captures:
1: constant.numeric.base.python
2: constant.numeric.value.python
3: constant.numeric.suffix.python
- match: \b(0[bB])((?:_?[01])*)
scope: meta.number.integer.binary.python
captures:
1: constant.numeric.base.python
2: constant.numeric.value.python
# complex
- match: |-
(?x)
(
# 1.j, 1.1j, 1.1e1j, 1.1e-1j, 1.e1j, 1.e-1 | 1e1j, 1e-1j
\b{{digits}} (\.)? {{digits}}? {{exponent}}?
# .1j, .1e1j, .1e-1j
| (\.) {{digits}} {{exponent}}?
)
([jJ])
scope: meta.number.imaginary.decimal.python
captures:
1: constant.numeric.value.python
2: punctuation.separator.decimal.python
3: punctuation.separator.decimal.python
4: constant.numeric.suffix.python
# floating point
- match: |-
(?x:
# 1., 1.1, 1.1e1, 1.1e-1, 1.e1, 1.e-1 | 1e1, 1e-1
\b{{digits}} (?: (\.) {{digits}}? {{exponent}}? | {{exponent}} )
# .1, .1e1, .1e-1
| (\.) {{digits}} {{exponent}}?
)
scope: meta.number.float.decimal.python constant.numeric.value.python
captures:
1: punctuation.separator.decimal.python
2: punctuation.separator.decimal.python
# integer
- match: \b([1-9]\d*|0)([lL])\b # py2
scope: meta.number.integer.decimal.python
captures:
1: constant.numeric.value.python
2: constant.numeric.suffix.python
- match: \b([1-9][\d_]*|0)\b
scope: meta.number.integer.decimal.python constant.numeric.value.python
modifiers:
- match: \b(?:(global)|(nonlocal))\b
captures:
1: storage.modifier.global.python
2: storage.modifier.nonlocal.python
push:
- include: line-continuation-or-pop
- match: ','
scope: punctuation.separator.storage-list.python
- include: name
- match: \S+
scope: invalid.illegal.name.storage.python
yields:
- match: \b(yield)(?:\s+(from))?\b
captures:
1: keyword.control.flow.yield.python
2: keyword.control.flow.yield-from.python
assignment-expression:
- match: :=
scope: keyword.operator.assignment.inline.python
illegal-assignment-expression:
- match: :=
scope: invalid.illegal.not-allowed-here.python
assignments:
- include: illegal-assignment-expression
- match: ':'
scope: punctuation.separator.annotation.variable.python
- match: \+=|-=|\*=|/=|//=|%=|@=|&=|\|=|\^=|>>=|<<=|\*\*=
scope: keyword.operator.assignment.augmented.python
- match: '=(?!=)'
scope: keyword.operator.assignment.python
operators:
- match: <>
scope: invalid.deprecated.operator.python
- match: <\=|>\=|\=\=|<|>|\!\=
scope: keyword.operator.comparison.python
- match: \+|\-|\*|\*\*|/|//|%|<<|>>|&|\||\^|~
scope: keyword.operator.arithmetic.python
- match: \b(and|in|is|not|or)\b
comment: keyword operators that evaluate to True or False
scope: keyword.operator.logical.python
- match: '@'
scope: keyword.operator.matrix.python
- match: ','
scope: punctuation.separator.sequence.python
allow-unpack-operators:
# Match unpacking operators, if present
- include: comments
- match: \*{3,}
scope: invalid.illegal.syntax.python
pop: true
- match: \*\*
scope: keyword.operator.unpacking.mapping.python
pop: true
- match: \*
scope: keyword.operator.unpacking.sequence.python
pop: true
- match: (?=\S)
pop: true
classes:
- match: '^\s*(class)\b'
captures:
1: keyword.declaration.class.python
push:
- meta_scope: meta.class.python
- include: line-continuation-or-pop
- match: ':'
scope: punctuation.section.class.begin.python
pop: true
- match: "(?={{identifier}})"
push:
- meta_content_scope: entity.name.class.python
- include: entity-name-class
- match: ''
pop: true
- match: \(
scope: punctuation.section.inheritance.begin.python
set:
- meta_scope: meta.class.inheritance.python
- match: \)
scope: punctuation.section.inheritance.end.python
set:
- include: line-continuation-or-pop
- match: ':'
scope: meta.class.python punctuation.section.class.begin.python
pop: true
- match: (?=\S)
pop: true
- match: ':'
scope: invalid.illegal.no-closing-parens.python
pop: true
- match: ','
scope: punctuation.separator.inheritance.python
- include: illegal-names-pop
- match: ({{identifier}}) *(=)
captures:
1: variable.parameter.class-inheritance.python
2: keyword.operator.assignment.python
- match: (?={{path}})
push:
- meta_scope: entity.other.inherited-class.python
- match: '{{identifier}}(?: *(\.) *)?'
captures:
1: punctuation.accessor.dot.python
- match: ''
pop: true
- include: expression-in-a-group
functions:
- match: '^\s*(?:(async)\s+)?(def)\b'
captures:
1: keyword.declaration.async.python
2: keyword.declaration.function.python
push:
- meta_scope: meta.function.python
- include: line-continuation-or-pop
- match: ':'
scope: punctuation.section.function.begin.python
pop: true
- match: "(?={{identifier}})"
push:
- meta_content_scope: entity.name.function.python
- include: entity-name-function
- match: ''
pop: true
- match: '(?=\()'
set:
- match: \(
scope: meta.function.parameters.python punctuation.section.parameters.begin.python
set: [function-parameters, allow-unpack-operators]
function-parameters:
- meta_content_scope: meta.function.parameters.python
- match: \)
scope: punctuation.section.parameters.end.python
set: function-after-parameters
- include: comments
- match: ','
scope: punctuation.separator.parameters.python
push: allow-unpack-operators
- match: /
scope: storage.modifier.positional-args-only.python
push:
- match: (?=[,)])
pop: true
- match: \S
scope: invalid.illegal.expected-comma.python
- match: '(?==)'
set:
- match: '='
scope: keyword.operator.assignment.python
set:
- meta_scope: meta.function.parameters.default-value.python
- match: '(?=[,)])'
set: [function-parameters, allow-unpack-operators]
- include: illegal-assignment-expression
- include: expression-in-a-group
- match: '(?=:)'
set:
- match: ':'
scope: punctuation.separator.annotation.parameter.python
set:
- meta_scope: meta.function.parameters.annotation.python
- match: '(?=[,)=])'
set: function-parameters
- include: illegal-assignment-expression
- include: expression-in-a-group
- include: function-parameters-tuple
- include: illegal-names
- match: '{{identifier}}'
scope: variable.parameter.python
- include: line-continuation
function-parameters-tuple:
# python 2 style tuple arguments
# removed from python 3 since PEP-3113
- match: \(
scope: punctuation.section.group.begin.python
push:
- meta_scope: meta.group.python
- match: \)
scope: punctuation.section.group.end.python
set: after-expression
- include: comments
- match: ','
scope: punctuation.separator.parameters.python
push: allow-unpack-operators
# default values should follow the argument
- match: '='
push:
- meta_scope: invalid.illegal.default-value.python
- match: '(?=[,)=])'
pop: true
# python 2 does not support type annotations
- match: '(?=:)'
push:
- meta_scope: invalid.illegal.annotation.python
- match: '(?=[,)=])'
pop: true
- include: illegal-names
- match: '{{identifier}}'
scope: variable.parameter.python
- include: line-continuation
function-after-parameters:
- meta_content_scope: meta.function.python
- match: (?=->)
set: [function-return-type, function-return-type-separator]
- include: function-terminator
function-return-type:
- meta_content_scope: meta.function.annotation.return.python
- include: illegal-assignment-expression
- include: function-terminator
- include: expression-in-a-statement
function-return-type-separator:
- match: ->
scope: punctuation.separator.annotation.return.python
pop: true
function-terminator:
- match: ':'
scope: meta.function.python punctuation.section.function.begin.python
pop: true
- include: line-continuation-or-pop
decorators:
- match: ^\s*(?=@)
push:
# Due to line continuations, we don't know whether this is a "function call" yet
- meta_content_scope: meta.annotation.python
- match: '@'
scope: punctuation.definition.annotation.python
- match: $
pop: true
- include: line-continuation-or-pop
- match: (?=\.?\s*{{path}}\s*\() # now we do
set: [after-expression, decorator-function-call-wrapper, qualified-name-until-leaf]
- match: (?=\.?\s*{{path}})
push: [decorator-wrapper, qualified-name-until-leaf]
- match: \S
scope: invalid.illegal.character.python
pop: true
decorator-wrapper:
- match: (\.)\s*
captures:
1: punctuation.accessor.dot.python
set:
- meta_scope: meta.qualified-name.python
- meta_content_scope: variable.annotation.python
- include: dotted-name-specials
- include: generic-names
- match: ''
pop: true
- match: ''
set:
- meta_scope: meta.qualified-name.python variable.annotation.python
- include: name-specials
- include: generic-names
- match: ''
pop: true
decorator-function-call-wrapper:
- meta_scope: meta.annotation.function.python
- match: \)
scope: punctuation.section.arguments.end.python
pop: true
- match: \(
scope: meta.annotation.function.python punctuation.section.arguments.begin.python
push: [decorator-function-call-arguments, allow-unpack-operators]
- match: (\.)\s*
captures:
1: punctuation.accessor.dot.python
push:
- meta_scope: meta.qualified-name.python
- meta_content_scope: variable.annotation.function.python
- include: dotted-name-specials
- include: generic-names
- match: ''
pop: true
- match: ''
push:
- meta_scope: meta.qualified-name.python variable.annotation.function.python
- include: name-specials
- include: generic-names
- match: ''
pop: true
decorator-function-call-arguments:
- clear_scopes: 1
- meta_content_scope: meta.annotation.arguments.python
- match: (?=\))
pop: true
- include: arguments
item-access:
- match: '(?={{path}}\s*\[)'
push:
- match: \]
scope: meta.item-access.python punctuation.section.brackets.end.python
set: after-expression
- match: '(?={{path}}\s*\[)'
push:
- meta_content_scope: meta.item-access.python
- match: '(?=\s*\[)'
pop: true
- include: qualified-name
- match: \[
scope: meta.item-access.python punctuation.section.brackets.begin.python
push:
- meta_content_scope: meta.item-access.arguments.python
- match: '(?=\])'
pop: true
- match: ':'
scope: punctuation.separator.slice.python
- include: expression-in-a-group
function-calls:
- match: '(?=(\.\s*)?{{path}}\s*\()'
push: [function-call-wrapper, qualified-name-until-leaf]
function-call-wrapper:
- meta_scope: meta.function-call.python
- match: (?=\() # need to remove meta.function-call.python from opening parens
set:
- match: \(
scope: punctuation.section.arguments.begin.python
set: [after-expression, function-call-arguments, allow-unpack-operators]
- match: (\.)\s*(?={{identifier}})
captures:
1: punctuation.accessor.dot.python
push:
- meta_scope: meta.qualified-name.python
- meta_content_scope: variable.function.python
- include: dotted-name-specials
- include: generic-names
- match: ''
pop: true
- match: (?={{identifier}})
push:
- meta_scope: meta.qualified-name.python variable.function.python
- include: name-specials
- include: generic-names
- match: ''
pop: true
function-call-arguments:
- meta_scope: meta.function-call.arguments.python
- match: \)
scope: punctuation.section.arguments.end.python
pop: true
- include: arguments
arguments:
- include: keyword-arguments
- match: ','
scope: punctuation.separator.arguments.python
push: allow-unpack-operators
- include: inline-for
- include: expression-in-a-group
keyword-arguments:
- match: '(?={{identifier}}\s*=(?!=))'
push:
- include: line-continuation-or-pop
- match: '='
scope: keyword.operator.assignment.python
set:
- include: illegal-assignment-expression
- match: (?=[,):])
pop: true
- include: expression-in-a-group
- include: illegal-names
- match: '{{identifier}}'
scope: variable.parameter.python
lambda:
- match: \b(lambda)(?=\s|:|$)
scope:
meta.function.inline.python
storage.type.function.inline.python
keyword.declaration.function.inline.python
push: [lambda-parameters, allow-unpack-operators]
lambda-parameters:
- meta_content_scope: meta.function.inline.parameters.python
- include: line-continuation-or-pop
- match: '\:'
scope: punctuation.section.function.begin.python
set:
# clear meta_scope
- match: ''
set:
- meta_scope: meta.function.inline.body.python
- include: illegal-assignment-expression
# We don't know whether we are within a grouped
# or line-statement context at this point.
# If we're in a group, the underlying context will take over
# at the end of the line.
- match: (?=[,:)}\]])|$
pop: true
- include: expression-in-a-statement
- match: ','
scope: punctuation.separator.parameters.python
push: allow-unpack-operators
- include: keyword-arguments
- include: function-parameters-tuple
- include: illegal-names
- match: '{{identifier}}'
scope: variable.parameter.python
- match: '\S'
scope: invalid.illegal.expected-parameter.python
groups:
- match: \(
scope: punctuation.section.group.begin.python
push:
- meta_scope: meta.group.python
- match: \)
scope: punctuation.section.group.end.python
set: after-expression
- match: ','
scope: punctuation.separator.tuple.python
- include: inline-for
- include: expression-in-a-group
tuples:
# We don't know for certain, whether a parenthesized expression is a tuple,
# so try looking ahead.
- match: (\()\s*(\))
scope: meta.sequence.tuple.empty.python
captures:
1: punctuation.section.sequence.begin.python
2: punctuation.section.sequence.end.python
push: after-expression
- match: \((?={{simple_expression}},|\s*\*{{path}})
scope: punctuation.section.sequence.begin.python
push: inside-tuple
# TODO generator
# - match: \((?:{{simple_expression}}for)
inside-tuple:
- meta_scope: meta.sequence.tuple.python
- match: \)
scope: punctuation.section.sequence.end.python
set: after-expression
- match: ','
scope: punctuation.separator.sequence.python
push: allow-unpack-operators
- include: inline-for
- include: expression-in-a-group
lists:
- match: (\[)\s*(\])
scope: meta.sequence.list.empty.python
captures:
1: punctuation.section.sequence.begin.python
2: punctuation.section.sequence.end.python
push: after-expression
- match: \[
scope: punctuation.section.sequence.begin.python
push: [inside-list, allow-unpack-operators]
inside-list:
- meta_scope: meta.sequence.list.python
- match: \]
scope: punctuation.section.sequence.end.python
set: after-expression
- match: ','
scope: punctuation.separator.sequence.python
push: allow-unpack-operators
- include: inline-for
- include: expression-in-a-group
dictionaries-and-sets:
# Dictionaries and set literals use the same punctuation,
# so we try looking ahead to determine whether we have a dict or a set.
- match: '(\{)\s*(\})'
scope: meta.mapping.empty.python
captures:
1: punctuation.section.mapping.begin.python
2: punctuation.section.mapping.end.python
push: after-expression
- match: \{(?={{simple_expression}}:|\s*\*\*)
scope: punctuation.section.mapping.begin.python
push: inside-dictionary
- match: \{(?={{simple_expression}}[,}]|\s*\*)
scope: punctuation.section.set.begin.python
push: inside-set
# If the expression is "more complex" or on the next line,
# fall back to default and determine later.
- match: \{
scope: punctuation.section.mapping-or-set.begin.python
push:
- meta_scope: meta.mapping-or-set.python
- match: \}
scope: punctuation.section.mapping-or-set.end.python
set: after-expression
- match: (?={{simple_expression}}:|\s*\*\*)
set: inside-dictionary
- match: (?={{simple_expression}}[,}]|\s*\*)
set: inside-set
- match: ','
scope: punctuation.separator.set.python
set: inside-set
- include: illegal-assignment-expression
- match: ':'
scope: punctuation.separator.mapping.key-value.python
set: inside-directory-value
- include: inline-for
- include: expression-in-a-group
inside-dictionary:
- meta_scope: meta.mapping.python
- match: \}
scope: punctuation.section.mapping.end.python
set: after-expression
- include: illegal-assignment-expression
- match: ':'
scope: punctuation.separator.mapping.key-value.python
set: inside-directory-value
- match: ','
scope: invalid.illegal.expected-colon.python
- match: \*\*
scope: keyword.operator.unpacking.mapping.python
push:
- match: (?=\})
pop: true
- match: ','
scope: punctuation.separator.mapping.python
pop: true
- include: expression-in-a-group
- include: comments
- match: (?=\S)
push:
- clear_scopes: 1
- meta_scope: meta.mapping.key.python
- match: \s*(?=\}|,|:)
pop: true
- include: expression-in-a-group
inside-directory-value:
- meta_content_scope: meta.mapping.python
- match: \}
scope: punctuation.section.mapping.end.python
set: after-expression
- match: (?=,)
set:
# clear meta scope from this match, because 'inside-directory' has it in meta_scope
- match: ','
scope: punctuation.separator.mapping.python
set: inside-dictionary
- match: (?=(?:async|for)\b)
push:
- match: (?=\})
pop: true
- match: ','
scope: invalid.illegal.unexpected-comma.python
- include: inline-for
- include: expression-in-a-group
- include: comments
- match: (?=\S)
push:
- clear_scopes: 1
- meta_content_scope: meta.mapping.value.python
- match: (?=\s*(\}|,|(?:async|for)\b))
pop: true
- include: expression-in-a-group
inside-set:
- meta_scope: meta.set.python
- match: \}
scope: punctuation.section.set.end.python
set: after-expression
- include: illegal-assignment-expression
- match: ':'
scope: invalid.illegal.colon-inside-set.python
- match: ','
scope: punctuation.separator.set.python
- match: \*
scope: keyword.operator.unpacking.sequence.python
push:
- match: (?=\})
pop: true
- match: ','
scope: punctuation.separator.set.python
pop: true
- include: expression-in-a-group
- include: inline-for
- include: expression-in-a-group
builtin-exceptions:
- match: |-
(?x)\b(
(?:
Arithmetic|Assertion|Attribute|BlockingIO|BrokenPipe|Buffer|ChildProcess|
Connection(?:Aborted|Refused|Reset)?|EOF|Environment|FileExists|
FileNotFound|FloatingPoint|Interrupted|IO|IsADirectoryError|
Import|Indentation|Index|Key|Lookup|Memory|Name|NotADirectory|
NotImplemented|OS|Overflow|Permission|ProcessLookup|Reference|
Runtime|Standard|Syntax|System|Tab|Timeout|Type|UnboundLocal|
Unicode(?:Encode|Decode|Translate)?|Value|VMS|Windows|ZeroDivision
)Error|
(?:(?:Pending)?Deprecation|Resource|Runtime|Syntax|User|Future|Import|Unicode|Bytes)?Warning|
(?:Base)?Exception|
SystemExit|StopIteration|NotImplemented|KeyboardInterrupt|GeneratorExit
)\b
scope: support.type.exception.python
builtin-functions:
- match: |-
(?x)\b(?:
__import__|all|abs|any|ascii|bin|callable|chr|classmethod|
compile|delattr|dir|divmod|enumerate|eval|filter|format|getattr|
globals|hasattr|hash|help|hex|id|input|isinstance|issubclass|iter|
len|locals|map|max|min|next|oct|open|ord|pow|property|range|
repr|reversed|round|setattr|sorted|staticmethod|
sum|super|type|vars|zip
# Python 2 functions
|apply|cmp|coerce|execfile|intern|raw_input|reduce|reload|unichr|xrange
# Python 3 functions
|breakpoint|exec
)\b
scope: support.function.builtin.python
builtin-types:
- match: |-
(?x)\b(?:
bool|bytearray|bytes|complex|dict|float|frozenset|int|
list|memoryview|object|set|slice|str|tuple
# Python 2 types
|basestring|long|unicode
# Python 2 types prone to conflicts
# |buffer|file
)\b
scope: support.type.python
name:
- match: '(?={{identifier}})'
push:
- include: name-specials
- match: '{{identifier_constant}}'
scope: variable.other.constant.python
- include: generic-names
- match: ''
pop: true
dotted-name:
- match: '\s*(\.)\s*(?={{identifier}})'
captures:
1: punctuation.accessor.dot.python
push:
- include: dotted-name-specials
- match: '{{identifier_constant}}'
scope: variable.other.constant.python
- include: generic-names
- match: ''
pop: true
qualified-name:
- match: '(?={{path}})'
push:
- meta_scope: meta.qualified-name.python
- include: name
- include: dotted-name
- match: ''
pop: true
- match: \.
scope: punctuation.accessor.dot.python
qualified-name-until-leaf:
# Push this together with another context to match a qualified name
# until the last non-special identifier (if any).
# This allows the leaf to be scoped individually.
- meta_scope: meta.qualified-name.python
# If a line continuation follows, this may or may not be the last leaf (most likley not though)
- match: (?={{identifier}}\s*(\.|\\))
push:
- include: name-specials
- include: generic-names
- match: ''
pop: true
- match: (\.)\s*(?={{identifier}}\s*(\.|\\))
captures:
1: punctuation.accessor.dot.python
push:
- include: dotted-name-specials
- include: generic-names
- match: ''
pop: true
- match: \.(?!\s*{{identifier}}) # don't match last dot
scope: punctuation.accessor.dot.python
- match: (?=\S|$)
pop: true
name-specials:
- include: builtin-functions
- include: builtin-types
- include: builtin-exceptions
- include: illegal-names
- include: magic-function-names
- include: magic-variable-names
- include: language-variables
dotted-name-specials:
- include: magic-function-names
- include: magic-variable-names
- include: illegal-names
entity-name-class:
- include: illegal-names
- include: generic-names
entity-name-function:
- include: magic-function-names
- include: illegal-names
- include: generic-names
generic-names:
- match: '{{identifier}}'
scope: meta.generic-name.python
illegal-names:
- match: \b{{illegal_names}}\b
scope: invalid.illegal.name.python
illegal-names-pop:
- match: \b{{illegal_names}}\b
scope: invalid.illegal.name.python
pop: true
language-variables:
- match: \b(self|cls)\b
scope: variable.language.python
- match: _(?!{{identifier_continue}})
scope: variable.language.python
line-continuation:
- match: (\\)(.*)$\n?
captures:
1: punctuation.separator.continuation.line.python
2: invalid.illegal.unexpected-text.python
# make sure to resume parsing at next line
push:
# This prevents strings after a continuation from being a docstring
- include: strings
- match: (?=\S|^\s*$|\n) # '\n' for when we matched a string earlier
pop: true
line-continuation-or-pop:
- include: line-continuation
- match: (?=\s*($|;|#))
pop: true
magic-function-names:
# https://docs.python.org/2/reference/datamodel.html
# https://docs.python.org/3/reference/datamodel.html
- match: |-
(?x)\b__(?:
# unary operators
invert|neg|pos|abs|
# binary operators
add|and|div|divmod|floordiv|lshift|mod|mul|or|pow|rshift|sub|truediv|xor|
contains|
# right-hand binary operators
radd|rand|rdiv|rdivmod|rfloordiv|rlshift|rmod|rmul|ror|rpow|rrshift|rsub|rtruediv|rxor|
# in-place operator assignments
iadd|iand|idiv|ifloordiv|ilshift|imod|imul|ior|ipow|irshift|isub|itruediv|ixor|
# comparisons
eq|ge|gt|le|lt|ne|
cmp|rcmp| # py2
# primary coercion
bool|str|
nonzero|unicode| # py2
# number coercion (converts something to a number)
bytes|complex|float|index|int|round|
long| # py2
# other "coercion"
format|len|length_hint|hash|repr|reversed|
coerce|hex|oct| # py2
fspath|
# iterator (and 'await')
iter|next|
aiter|anext|
await|
# attribute and item access
delattr|delitem|delslice|
getattr|getattribute|getitem|getslice|
setattr|setitem|setslice|
dir|missing|
# context manager
enter|exit|
aenter|aexit|
# other class magic
call|del|init|new|init_subclass|
instancecheck|subclasscheck|
# pickling
getnewargs|getnewargs_ex|getstate|setstate|reduce|reduce_ex|
# descriptors
delete|get|set|set_name|
# class-specific
subclasses|
# dataclasses (PEP 557)
post_init|
# for typing core support (PEP 560)
class_getitem|mro_entries
)__\b
comment: these methods have magic interpretation by python and are generally called indirectly through syntactic constructs
scope: support.function.magic.python
magic-variable-names:
# magic variables which a class/module/object may have.
# https://docs.python.org/3/library/inspect.html#types-and-members
# https://docs.python.org/3/reference/datamodel.html#object.__slots__
# https://docs.python.org/3/reference/datamodel.html#preparing-the-class-namespace
- match: |-
(?x)\b__(?:
# generic object
class|dict|doc|module|name|
# module-specific / global
all|file|package|
# functions & methods
annotations|closure|code|defaults|func|globals|kwdefaults|self|qualname|
# classes (attributes)
bases|prepare|slots|metaclass|mro|
# Python 2
members|methods
)__\b
scope: support.variable.magic.python
docstrings:
- match: ^\s*(?=(?i)(ur|ru|u|r)?("""|'''))
push:
- match: (?i)(u)?("""|''')
captures:
1: storage.type.string.python
2: punctuation.definition.comment.begin.python
set:
- meta_scope: comment.block.documentation.python
- include: escaped-unicode-char
- include: escaped-char
- match: '\2'
scope: punctuation.definition.comment.end.python
pop: true
- match: (?i)(u?ru?)("""|''')
captures:
1: storage.type.string.python
2: punctuation.definition.comment.begin.python
set:
- meta_scope: comment.block.documentation.python
- match: '\2'
scope: punctuation.definition.comment.end.python
pop: true
escaped-char:
- match: '(\\x\h{2})|(\\[0-7]{1,3})|(\\[\\"''abfnrtv])'
captures:
1: constant.character.escape.hex.python
2: constant.character.escape.octal.python
3: constant.character.escape.python
- match: \\. # deprecated in 3.6 and will eventually be a syntax error
scope: invalid.deprecated.character.escape.python
escaped-unicode-char:
- match: '(\\U\h{8})|(\\u\h{4})|(\\N\{[-a-zA-Z ]+\})'
captures:
1: constant.character.escape.unicode.16-bit-hex.python
2: constant.character.escape.unicode.32-bit-hex.python
3: constant.character.escape.unicode.name.python
escaped-fstring-escape:
# special-case the '\{{' sequence because it has higher priority than the deprecated '\{'
- match: (\\)(\{\{|\}\})
scope: constant.character.escape.backslash.regexp
captures:
1: invalid.deprecated.character.escape.python
2: constant.character.escape.python
line-continuation-inside-string:
- match: (\\)$\n?
captures:
1: punctuation.separator.continuation.line.python
- match: \n
scope: invalid.illegal.unclosed-string.python
set: after-expression
line-continuation-inside-block-string:
- match: \\$
scope: punctuation.separator.continuation.line.python
constant-placeholder:
- match: |- # printf style
(?x)
%
( \( ({{identifier}}) \) )? # mapping key
\#? # alternate form
0? # pad with zeros
\-? # left-adjust
\ ? # implicit sign
[+-]? # sign
(\d*|\*) # width
(\. (\d*|\*))? # precision
[hlL]? # length modifier (but ignored)
[acdeEfFgGiorsuxX%]
scope: constant.other.placeholder.python
captures:
2: variable.other.placeholder.python
- match: '{{strftime_spec}}'
scope: constant.other.placeholder.python
- match: '\{\{|\}\}'
scope: constant.character.escape.python
- include: formatting-syntax
formatting-syntax:
# https://docs.python.org/3.6/library/string.html#formatstrings
# Technically allows almost every character for the key,
# but those are rarely used if ever.
- match: |- # simple form
(?x)
(\{)
(?: [\w.\[\]]+)? # field_name
( ! [ars])? # conversion
(?: (:) ({{format_spec}}| # format_spec OR
[^}%]*%.[^}]*) # any format-like string
)?
(\})
scope: constant.other.placeholder.python
captures:
1: punctuation.definition.placeholder.begin.python
2: storage.modifier.conversion.python
3: punctuation.separator.format-spec.python
4: meta.format-spec.python constant.other.format-spec.python
5: punctuation.definition.placeholder.end.python
- match: (?=\{[^{}"']+\{[^"']*\}) # complex (nested) form
branch_point: formatting-syntax-branch
branch:
- formatting-syntax-complex
- formatting-syntax-fallback
formatting-syntax-fallback:
- match: \{
scope: meta.debug.formatting-syntax-fallback.python
pop: true
formatting-syntax-complex:
- match: \{
scope: punctuation.definition.placeholder.begin.python
set:
- meta_scope: constant.other.placeholder.python
- match: \}
scope: punctuation.definition.placeholder.end.python
pop: true
# TODO could match numeric indices or everything else as a key
# and also [] indexing
- match: '![ars]'
scope: storage.modifier.conversion.python
- match: ':'
scope: punctuation.separator.format-spec.python
push:
- meta_content_scope: meta.format-spec.python constant.other.format-spec.python
- match: (?=\})
pop: true
- match: (?=\{)
push: formatting-syntax-complex
- match: '[{"''\n]'
fail: formatting-syntax-branch
f-string-content:
# https://www.python.org/dev/peps/pep-0498/
# https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings
- match: \{\{|\}\}
scope: constant.character.escape.python
- match: \{\s*\}
scope: invalid.illegal.empty-expression.python
- match: (?=\{)
push: f-string-replacement
- match: \}
scope: invalid.illegal.stray-brace.python
f-string-content-with-regex:
# Same as f-string-content, but will reset the entire scope stack
# and has an additional match.
- match: \\(\{\{|\}\})
scope: constant.character.escape.backslash.regexp
captures:
1: constant.character.escape.python
- match: \{\{|\}\}
scope: constant.character.escape.python
- match: \{\s*\}
scope: invalid.illegal.empty-expression.python
- match: (?=\{)
push: f-string-replacement-reset
- match: \}
scope: invalid.illegal.stray-brace.python
f-string-replacement:
- clear_scopes: 1
- match: \}
scope: meta.interpolation.python punctuation.section.interpolation.end.python
pop: true
- match: \{
scope: punctuation.section.interpolation.begin.python
push:
- meta_scope: meta.interpolation.python
- match: (?=\})
pop: true
- match: '![ars]'
scope: storage.modifier.conversion.python
- match: =
scope: storage.modifier.debug.python
- match: ':'
push:
- meta_scope: meta.format-spec.python constant.other.format-spec.python
# Because replacements can also be used *within* the format-spec,
# basically any character is valid and matching {{format_spec}} is useless.
# - match: '{{format_spec}}'
- match: (?=\})
pop: true
- include: f-string-content
- match: ''
push:
- meta_content_scope: source.python.embedded
- match: (?==?(![^=]|:|\}))
pop: true
- match: \\
scope: invalid.illegal.backslash-in-fstring.python
- include: inline-for
- include: expression-in-a-group
f-string-replacement-reset:
# Same as f-string-replacement, but with clear_scopes: true
- clear_scopes: true
- meta_scope: source.python meta.string.interpolated.python
- match: \}
scope: meta.interpolation.python punctuation.section.interpolation.end.python
pop: true
- match: \{
scope: punctuation.section.interpolation.begin.python
push:
- meta_scope: meta.interpolation.python
- match: (?=\})
pop: true
- match: '![ars]'
scope: storage.modifier.conversion.python
- match: ':'
push:
- meta_scope: meta.format-spec.python constant.other.format-spec.python
- match: (?=\})
pop: true
- include: f-string-content
- match: ''
push:
- meta_content_scope: source.python.embedded
- match: (?=![^=]|:|\})
pop: true
- match: \\
scope: invalid.illegal.backslash-in-fstring.python
- include: inline-for
- include: expression-in-a-group
string-quoted-double-block:
# Triple-quoted capital R raw string, unicode or not, no syntax embedding
- match: '([uU]?R)(""")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- include: escaped-unicode-char
# Triple-quoted capital R raw string, bytes, no syntax embedding
- match: '([bB]R|R[bB])(""")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
# Triple-quoted raw string, unicode or not, will detect SQL, otherwise regex
- match: '([uU]?r)(""")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.block.python
- match: '(?={{sql_indicator}})'
set:
- meta_scope: meta.string.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
push: scope:source.sql
with_prototype:
- match: '(?=""")'
pop: true
- include: escaped-unicode-char
- include: constant-placeholder
- match: '(?=\S)'
set:
- meta_scope: meta.string.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
push: scope:source.regexp.python
with_prototype:
- match: '(?=""")'
pop: true
- include: escaped-unicode-char
# Triple-quoted raw string, bytes, will use regex
- match: '([bB]r|r[bB])(""")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
embed: scope:source.regexp.python
escape: (?=""")
# Triple-quoted raw f-string
- match: ([fF]R|R[fF])(""")
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.double.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- include: f-string-content
# Triple-quoted raw f-string, treated as regex
- match: ([fF]r|r[fF])(""")
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.double.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
push: scope:source.regexp.python
with_prototype:
- match: '(?=""")'
pop: true
- include: f-string-content-with-regex
# Triple-quoted f-string
- match: ([fF])(""")
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.double.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-block-string
- include: escaped-fstring-escape
- include: escaped-unicode-char
- include: escaped-char
- include: f-string-content
# Triple-quoted string, unicode or not, will detect SQL
- match: '([uU]?)(""")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.block.python
- match: '(?={{sql_indicator}})'
set:
- meta_scope: meta.string.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
push: scope:source.sql
with_prototype:
- match: '(?=""")'
pop: true
- include: line-continuation-inside-block-string
- include: escaped-unicode-char
- include: escaped-char
- include: constant-placeholder
- match: '(?=\S)'
set:
- meta_scope: meta.string.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-block-string
- include: escaped-unicode-char
- include: escaped-char
- include: constant-placeholder
# Triple-quoted string, bytes, no syntax embedding
- match: '([bB])(""")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.block.python
- match: '"""'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-block-string
- include: escaped-char
- include: constant-placeholder
string-quoted-double:
# Single-line capital R raw string, unicode or not, no syntax embedding
- match: '([uU]?R)(")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
# Single-line capital R raw string, bytes, no syntax embedding
- match: '([bB]R|R[bB])(")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
# Single-line raw string, unicode or not, starting with a SQL keyword
- match: '([uU]?r)(")(?={{sql_indicator}})'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
push: scope:source.sql
with_prototype:
- match: '(?="|\n)'
pop: true
- include: constant-placeholder
- include: line-continuation-inside-string
# Single-line raw string, unicode or not, treated as regex
- match: '([uU]?r)(")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
push: scope:source.regexp.python
with_prototype:
- match: '(?="|\n)'
pop: true
- include: line-continuation-inside-string
# Single-line raw string, bytes, treated as regex
- match: '([bB]r|r[bB])(")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
embed: scope:source.regexp.python
escape: (?="|\n)
# Single-line raw f-string
- match: (R[fF]|[fF]R)(")
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- include: f-string-content
# Single-line raw f-string, treated as regex
- match: (r[fF]|[fF]r)(")
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
push: scope:source.regexp.python
with_prototype:
- match: '(?="|\n)'
pop: true
- include: line-continuation-inside-string
- include: f-string-content-with-regex
# Single-line f-string
- match: ([fF])(")
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: escaped-fstring-escape
- include: escaped-unicode-char
- include: escaped-char
- include: line-continuation-inside-string
- include: f-string-content
# Single-line string, unicode or not, starting with a SQL keyword
- match: '([uU]?)(")(?={{sql_indicator}})'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
push: scope:source.sql
with_prototype:
- match: '(?="|\n)'
pop: true
- include: escaped-unicode-char
- include: escaped-char
- include: line-continuation-inside-string
- include: constant-placeholder
# Single-line string, unicode or not
- match: '([uU]?)(")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: escaped-unicode-char
- include: escaped-char
- include: line-continuation-inside-string
- include: constant-placeholder
# Single-line string, bytes
- match: '([bB])(")'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.double.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.double.python
- match: '"'
scope: punctuation.definition.string.end.python
set: after-expression
- include: escaped-char
- include: line-continuation-inside-string
- include: constant-placeholder
string-quoted-single-block:
# Triple-quoted capital R raw string, unicode or not, no syntax embedding
- match: ([uU]?R)(''')
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
# Triple-quoted capital R raw string, bytes, no syntax embedding
- match: ([bB]R|R[bB])(''')
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
# Triple-quoted raw string, unicode or not, will detect SQL, otherwise regex
- match: ([uU]?r)(''')
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.block.python
- match: '(?={{sql_indicator}})'
set:
- meta_scope: meta.string.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
push: scope:source.sql
with_prototype:
- match: (?=''')
pop: true
- include: escaped-unicode-char
- include: escaped-char
- include: constant-placeholder
- match: '(?=\S)'
set:
- meta_scope: meta.string.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
push: scope:source.regexp.python
with_prototype:
- match: (?=''')
pop: true
- include: escaped-unicode-char
# Triple-quoted raw string, bytes, will use regex
- match: ([bB]r|r[bB])(''')
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
embed: scope:source.regexp.python
escape: (?=''')
# Triple-quoted raw f-string
- match: ([fF]R|R[fF])(''')
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.single.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
- include: f-string-content
# Triple-quoted raw f-string, treated as regex
- match: ([fF]r|r[fF])(''')
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.single.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
push: scope:source.regexp.python
with_prototype:
- match: (?=''')
pop: true
- include: f-string-content-with-regex
# Triple-quoted f-string
- match: ([fF])(''')
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.single.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-block-string
- include: escaped-fstring-escape
- include: escaped-unicode-char
- include: escaped-char
- include: f-string-content
# Triple-quoted string, unicode or not, will detect SQL
- match: ([uU]?)(''')
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.block.python
- match: '(?={{sql_indicator}})'
set:
- meta_scope: meta.string.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
- match: ''
push: scope:source.sql
with_prototype:
- match: (?=''')
pop: true
- include: line-continuation-inside-block-string
- include: escaped-unicode-char
- include: escaped-char
- include: constant-placeholder
- match: '(?=\S)'
set:
- meta_scope: meta.string.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-block-string
- include: escaped-unicode-char
- include: escaped-char
- include: constant-placeholder
# Triple-quoted string, bytes, no syntax embedding
- match: ([bB])(''')
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.block.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.block.python
- match: "'''"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-block-string
- include: escaped-char
- include: constant-placeholder
string-quoted-single:
# Single-line capital R raw string, unicode or not, no syntax embedding
- match: '([uU]?R)('')'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
# Single-line capital R raw string, bytes, no syntax embedding
- match: '([bB]R|R[bB])('')'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
# Single-line raw string, unicode or not, starting with a SQL keyword
- match: '([uU]?r)('')(?={{sql_indicator}})'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
push: scope:source.sql
with_prototype:
- match: '(?=''|\n)'
pop: true
- include: line-continuation-inside-string
- include: constant-placeholder
# Single-line raw string, unicode or not, treated as regex
- match: '([uU]?r)('')'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
push: scope:source.regexp.python
with_prototype:
- match: '(?=''|\n)'
pop: true
- include: line-continuation-inside-string
# Single-line raw string, bytes, treated as regex
- match: '([bB]r|r[bB])('')'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
push: scope:source.regexp.python
with_prototype:
- match: '(?=''|\n)'
pop: true
- include: line-continuation-inside-string
# Single-line raw f-string
- match: ([fF]R|R[fF])(')
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- include: f-string-content
# Single-line raw f-string, treated as regex
- match: ([fF]r|r[fF])(')
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
push: scope:source.regexp.python
with_prototype:
- match: (?='|\n)
pop: true
- include: line-continuation-inside-string
- include: f-string-content-with-regex
# Single-line f-string
- match: ([fF])(')
captures:
1: storage.type.string.python
2: meta.string.interpolated.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.interpolated.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: escaped-fstring-escape
- include: escaped-unicode-char
- include: escaped-char
- include: line-continuation-inside-string
- include: f-string-content
# Single-line string, unicode or not, starting with a SQL keyword
- match: '([uU]?)('')(?={{sql_indicator}})'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: line-continuation-inside-string
- match: ''
push: scope:source.sql
with_prototype:
- match: '(?=''|\n)'
pop: true
- include: escaped-unicode-char
- include: escaped-char
- include: line-continuation-inside-string
- include: constant-placeholder
# Single-line string, unicode or not
- match: '([uU]?)('')'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: escaped-unicode-char
- include: escaped-char
- include: line-continuation-inside-string
- include: constant-placeholder
# Single-line string, bytes
- match: '([bB])('')'
captures:
1: storage.type.string.python
2: meta.string.python string.quoted.single.python punctuation.definition.string.begin.python
push:
- meta_content_scope: meta.string.python string.quoted.single.python
- match: "'"
scope: punctuation.definition.string.end.python
set: after-expression
- include: escaped-char
- include: line-continuation-inside-string
- include: constant-placeholder
strings:
# block versions must be matched first
- include: string-quoted-double-block
- include: string-quoted-double
- include: string-quoted-single-block
- include: string-quoted-single
inline-for:
- match: \b(?:(async)\s+)?(for)\b
captures:
1: storage.modifier.async.python
2: keyword.control.loop.for.generator.python
push:
- include: comments
- meta_scope: meta.expression.generator.python
- match: \bin\b
scope: keyword.control.loop.for.in.python
pop: true
- match: '(?=[)\]}])'
scope: invalid.illegal.missing-in.python
pop: true
- include: illegal-names-pop
- include: target-list
inline-if:
- match: \bif\b
scope: keyword.control.conditional.if.python
- match: \belse\b
scope: keyword.control.conditional.else.python
target-list:
- match: ','
scope: punctuation.separator.target-list.python
- match: \(
scope: punctuation.section.target-list.begin.python
push:
- include: comments
- match: ','
scope: punctuation.separator.target-list.python
- match: \)
scope: punctuation.section.target-list.end.python
pop: true
- include: target-list
- include: name
- include: name