%YAML 1.2
---
# TOML v1.0 — sublime-syntax v1 (syntect-compatible)
# Reference: https://toml.io/en/v1.0.0
name: TOML
file_extensions: [toml, tml, Cargo.lock, Pipfile, poetry.lock, uv.lock]
scope: source.toml

contexts:
  main:
    - include: comments
    - include: array-table
    - include: table
    - include: key-value

  comments:
    - match: '#.*$'
      scope: comment.line.number-sign.toml

  # [[array.of.tables]]
  array-table:
    - match: '^\s*(\[\[)\s*'
      captures:
        1: punctuation.definition.table.array.begin.toml
      push: array-table-name

  array-table-name:
    - meta_content_scope: entity.name.section.toml
    - match: '\s*(\]\])'
      captures:
        1: punctuation.definition.table.array.end.toml
      pop: true
    - match: '\.'
      scope: punctuation.accessor.dot.toml
    - include: quoted-key

  # [table]
  table:
    - match: '^\s*(\[)\s*'
      captures:
        1: punctuation.definition.table.begin.toml
      push: table-name

  table-name:
    - meta_content_scope: entity.name.section.toml
    - match: '\s*(\])'
      captures:
        1: punctuation.definition.table.end.toml
      pop: true
    - match: '\.'
      scope: punctuation.accessor.dot.toml
    - include: quoted-key

  quoted-key:
    - match: '"'
      scope: punctuation.definition.string.begin.toml
      push: basic-string
    - match: "'"
      scope: punctuation.definition.string.begin.toml
      push: literal-string

  # key = value (bare, quoted, and dotted)
  key-value:
    - match: '([A-Za-z0-9_-]+)\s*(\.)'
      captures:
        1: entity.name.tag.toml
        2: punctuation.accessor.dot.toml
    - match: '([A-Za-z0-9_-]+)\s*(=)'
      captures:
        1: entity.name.tag.toml
        2: keyword.operator.assignment.toml
      push: value
    - match: '("(?:[^"\\]|\\.)*")\s*(=)'
      captures:
        1: entity.name.tag.toml
        2: keyword.operator.assignment.toml
      push: value
    - match: "('(?:[^'])*')\\s*(=)"
      captures:
        1: entity.name.tag.toml
        2: keyword.operator.assignment.toml
      push: value

  value:
    - include: comments
    # Multiline strings (must precede single-line)
    - match: '"""'
      scope: punctuation.definition.string.begin.toml
      set: multiline-basic-string
    - match: "'''"
      scope: punctuation.definition.string.begin.toml
      set: multiline-literal-string
    # Single-line strings
    - match: '"'
      scope: punctuation.definition.string.begin.toml
      set: basic-string
    - match: "'"
      scope: punctuation.definition.string.begin.toml
      set: literal-string
    # Booleans
    - match: '\b(true|false)\b'
      scope: constant.language.boolean.toml
      pop: true
    # Datetime (full offset, local, date-only, time-only)
    - match: '\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?'
      scope: constant.other.datetime.toml
      pop: true
    - match: '\d{4}-\d{2}-\d{2}'
      scope: constant.other.date.toml
      pop: true
    - match: '\d{2}:\d{2}:\d{2}(\.\d+)?'
      scope: constant.other.time.toml
      pop: true
    # Special floats
    - match: '[+-]?(inf|nan)\b'
      scope: constant.numeric.float.toml
      pop: true
    # Hex, octal, binary integers
    - match: '0x[\da-fA-F][\da-fA-F_]*'
      scope: constant.numeric.integer.hexadecimal.toml
      pop: true
    - match: '0o[0-7][0-7_]*'
      scope: constant.numeric.integer.octal.toml
      pop: true
    - match: '0b[01][01_]*'
      scope: constant.numeric.integer.binary.toml
      pop: true
    # Floats (before integers)
    - match: '[+-]?\d[\d_]*(\.\d[\d_]*)?[eE][+-]?\d[\d_]*'
      scope: constant.numeric.float.toml
      pop: true
    - match: '[+-]?\d[\d_]*\.\d[\d_]*'
      scope: constant.numeric.float.toml
      pop: true
    # Integers
    - match: '[+-]?\d[\d_]*'
      scope: constant.numeric.integer.decimal.toml
      pop: true
    # Inline table
    - match: '\{'
      scope: punctuation.definition.table.begin.toml
      set: inline-table
    # Array
    - match: '\['
      scope: punctuation.definition.array.begin.toml
      set: array
    - match: '$'
      pop: true

  basic-string:
    - meta_scope: string.quoted.double.toml
    - match: '\\[btnfr"\\]'
      scope: constant.character.escape.toml
    - match: '\\u\h{4}'
      scope: constant.character.escape.unicode.toml
    - match: '\\U\h{8}'
      scope: constant.character.escape.unicode.toml
    - match: '"'
      scope: punctuation.definition.string.end.toml
      pop: true

  literal-string:
    - meta_scope: string.quoted.single.toml
    - match: "'"
      scope: punctuation.definition.string.end.toml
      pop: true

  multiline-basic-string:
    - meta_scope: string.quoted.double.block.toml
    - match: '\\[btnfr"\\]'
      scope: constant.character.escape.toml
    - match: '\\u\h{4}'
      scope: constant.character.escape.unicode.toml
    - match: '\\U\h{8}'
      scope: constant.character.escape.unicode.toml
    - match: '"""'
      scope: punctuation.definition.string.end.toml
      pop: true

  multiline-literal-string:
    - meta_scope: string.quoted.single.block.toml
    - match: "'''"
      scope: punctuation.definition.string.end.toml
      pop: true

  array:
    - include: comments
    - match: ','
      scope: punctuation.separator.toml
    - match: '\]'
      scope: punctuation.definition.array.end.toml
      pop: true
    - include: value

  inline-table:
    - include: comments
    - include: key-value
    - match: ','
      scope: punctuation.separator.toml
    - match: '\}'
      scope: punctuation.definition.table.end.toml
      pop: true
