diff --git a/Python/Python.sublime-syntax b/Python/Python.sublime-syntax new file mode 100644 index 0000000..4967e7c --- /dev/null +++ b/Python/Python.sublime-syntax @@ -0,0 +1,2271 @@ +%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|print + )\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