Skip to content

Instantly share code, notes, and snippets.

@existentialmutt
Last active February 19, 2022 21:49
Show Gist options
  • Save existentialmutt/d27cc43cfeb6f98c1dde846e4e36c63b to your computer and use it in GitHub Desktop.
Save existentialmutt/d27cc43cfeb6f98c1dde846e4e36c63b to your computer and use it in GitHub Desktop.

Revisions

  1. existentialmutt revised this gist Oct 24, 2020. 1 changed file with 19 additions and 1 deletion.
    20 changes: 19 additions & 1 deletion Ruby.sublime-syntax
    Original file line number Diff line number Diff line change
    @@ -1423,9 +1423,27 @@ contexts:
    - match: ^\s*\2$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: scope:text.html.ruby
    - include: scope:text.html.basic
    - include: interpolated-ruby
    - include: escaped-char
    - match: "<%+#"
    scope: punctuation.definition.comment.erb
    push:
    - meta_scope: comment.block.erb
    - match: "%>"
    pop: true
    - match: "<%+(?!>)[-=]?"
    scope: punctuation.section.embedded.ruby
    push:
    - meta_scope: source.ruby.rails.embedded.html
    - match: "-?%>"
    scope: punctuation.section.embedded.ruby
    pop: true
    - match: (#).*?(?=-?%>)
    scope: comment.line.number-sign.ruby
    captures:
    1: punctuation.definition.comment.ruby
    - include: "Ruby on Rails.sublime-syntax"

    heredoc-haml:
    - meta_scope: string.unquoted.embedded.haml.ruby
  2. existentialmutt created this gist Oct 23, 2020.
    1,536 changes: 1,536 additions & 0 deletions Ruby.sublime-syntax
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1536 @@
    %YAML 1.2
    ---
    name: Ruby (Custom)
    # TODO: unresolved issues
    #
    # text:
    # "p << end
    # print me!
    # end"
    # symptoms:
    # not recognized as a heredoc
    # solution:
    # there is no way to distinguish perfectly between the << operator and the start
    # of a heredoc. Currently, we require assignment to recognize a heredoc. More
    # refinement is possible.
    # • Heredocs with indented terminators (<<-) are always distinguishable, however.
    # • Nested heredocs are not really supportable at present
    #
    # text:
    # print <<-'THERE'
    # This is single quoted.
    # The above used #{Time.now}
    # THERE
    # symtoms:
    # From Programming Ruby p306; should be a non-interpolated heredoc.
    #
    # text:
    # "a\332a"
    # symptoms:
    # '\332' is not recognized as slash3.. which should be octal 332.
    # solution:
    # plain regexp.. should be easy.
    #
    # text:
    # val?(a):p(b)
    # val?'a':'b'
    # symptoms:
    # ':p' is recognized as a symbol.. its 2 things ':' and 'p'.
    # :'b' has same problem.
    # solution:
    # ternary operator rule, precedence stuff, symbol rule.
    # but also consider 'a.b?(:c)' ??
    file_extensions:
    - rb
    - Appfile
    - Appraisals
    - Berksfile
    - Brewfile
    - capfile
    - cgi
    - Cheffile
    - config.ru
    - Deliverfile
    - Fastfile
    - fcgi
    - Gemfile
    - gemspec
    - Guardfile
    - irbrc
    - jbuilder
    - Podfile
    - podspec
    - prawn
    - rabl
    - rake
    - Rakefile
    - Rantfile
    - rbx
    - rjs
    - ruby.rail
    - Scanfile
    - simplecov
    - Snapfile
    - thor
    - Thorfile
    - Vagrantfile
    first_line_match: ^#!\s*/.*\bj?ruby\b
    scope: source.ruby.custom
    variables:
    identifier: '\b[[:alpha:]_][[:alnum:]_]*\b'
    method_punctuation: '(?:[?!]|=(?![>=]))?'
    method_name: '{{identifier}}{{method_punctuation}}'
    path_lookahead: '(::)?({{identifier}}(\.|::))*{{identifier}}'

    contexts:
    main:
    - include: expressions

    expressions:
    - include: class
    - include: module
    - include: constants
    - include: invalid
    - include: blocks
    - include: keywords
    - include: well-known-methods
    - include: variables
    - include: method
    - include: strings
    - include: comments
    - include: data-section
    - include: heredocs
    - include: operators
    - include: identifiers-accessors

    comments:
    # multiline comments
    - match: ^=begin
    scope: punctuation.definition.comment.ruby
    push:
    - meta_scope: comment.block.documentation.ruby
    - match: ^=end
    scope: punctuation.definition.comment.ruby
    pop: true
    - match: '(#).*$\n?'
    scope: comment.line.number-sign.ruby
    captures:
    1: punctuation.definition.comment.ruby

    class:
    # Defining a class method
    - match: \b(class)\b(?=\s*<)
    scope: keyword.control.class.ruby
    - match: '\b(class)\b'
    scope: meta.class.ruby keyword.control.class.ruby
    push: class-declaration

    class-declaration:
    - meta_content_scope: meta.class.ruby
    - match: '(?={{path_lookahead}})'
    set: class-name
    # Escape if no valid match
    - match: (?=\S)
    pop: true

    class-name:
    - meta_content_scope: meta.class.ruby entity.name.class.ruby
    - include: name-parts
    - match: ''
    set: class-inheritance

    class-inheritance:
    - meta_content_scope: meta.class.ruby
    - match: '<'
    scope: punctuation.separator.inheritance.ruby
    set:
    - meta_content_scope: meta.class.ruby
    - match: '(?={{path_lookahead}})'
    set:
    - meta_content_scope: meta.class.ruby entity.other.inherited-class.ruby
    - include: name-parts
    - match: ''
    pop: true
    # Escape if no valid match
    - match: '(?=\S)'
    pop: true
    # Escape if no valid match
    - match: '(?=\S)'
    pop: true

    module:
    - match: \b(module)\b
    scope: meta.module.ruby keyword.control.module.ruby
    push: module-declaration

    module-declaration:
    - meta_content_scope: meta.module.ruby
    - match: '(?=(::)?({{identifier}}::)*{{identifier}})'
    set:
    - meta_content_scope: meta.module.ruby entity.name.module.ruby
    - include: name-parts
    - match: ''
    pop: true
    # Escape if no valid match
    - match: (?=\S)
    pop: true

    name-parts:
    - match: '::'
    scope: punctuation.accessor.ruby
    - match: '\.'
    scope: punctuation.accessor.ruby
    - match: '({{identifier}})(::|\.)'
    captures:
    1: support.other.namespace.ruby
    2: punctuation.accessor.ruby
    - match: '{{identifier}}'

    invalid:
    # else if is a common mistake carried over from other languages. it works if you put in a second end, but it’s never what you want.
    - match: \belse\s+if\b
    scope: invalid.deprecated.ruby

    constants:
    # constant definition, handles multiple definitions on a single line 'APPLE, ORANGE= 1, 2'
    - match: '\b([[:upper:]]\w*)(?=(\s*,\s*[[:upper:]]\w*)*\s*=(?![=\>]))'
    scope: meta.constant.ruby entity.name.constant.ruby
    # Ruby 1.9 symbols
    - match: '{{identifier}}[?!]?(:)(?!:)'
    scope: constant.other.symbol.ruby
    captures:
    1: punctuation.definition.constant.ruby
    push: try-regex
    - match: '\b(nil|true|false)\b(?![?!])'
    scope: constant.language.ruby
    - match: '\b(__(FILE|LINE|ENCODING)__|self)\b(?![?!])'
    scope: variable.language.ruby
    - match: '\b(0[xX]\h(_?\h)*|\d(_?\d)*(\.(?![^[:space:][:digit:]])(_?\d)*)?([eE][-+]?\d(_?\d)*)?|0[bB][01]+)\b'
    scope: constant.numeric.ruby
    - match: ":'"
    scope: punctuation.definition.constant.ruby
    push:
    - meta_scope: constant.other.symbol.single-quoted.ruby
    - match: "'"
    scope: punctuation.definition.constant.ruby
    pop: true
    - match: '\\[''\\]'
    scope: constant.character.escape.ruby
    - match: ':"'
    scope: punctuation.definition.constant.ruby
    push:
    - meta_scope: constant.other.symbol.double-quoted.ruby
    - match: '"'
    scope: punctuation.definition.constant.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    # Unquoted symbols
    - match: |-
    (?x:
    (:)
    (
    {{identifier}}{{method_punctuation}}|
    ===?|
    >[>=]?|
    <[<=]?|
    <=>|
    [%&`/\|]|
    \*\*?|
    =?~|
    [-+]@?|
    \[\]=?|
    @@?{{identifier}}
    )
    )
    scope: constant.other.symbol.ruby
    captures:
    1: punctuation.definition.constant.ruby
    # matches questionmark-letters.
    #
    # examples (1st alternation = hex):
    # ?\x1 ?\x61
    #
    # examples (2nd alternation = octal):
    # ?\0 ?\07 ?\017
    #
    # examples (3rd alternation = escaped):
    # ?\n ?\b
    #
    # examples (4th alternation = meta-ctrl):
    # ?\C-a ?\M-a ?\C-\M-\C-\M-a
    #
    # examples (4th alternation = normal):
    # ?a ?A ?0
    # ?* ?" ?(
    # ?. ?#
    #
    # the negative lookbehind prevents against matching
    # p(42.tainted?)
    - match: '\?(\\(x\h{1,2}\b|0[0-7]{0,2}\b|[^x0MC]\b)|(\\[MC]-)+\w\b|[a-zA-Z0-9_]\b(?!\s*:)|[^a-zA-Z0-9_\s\\])'
    scope: constant.numeric.ruby

    blocks:
    - match: \b(do)\b\s*
    captures:
    1: keyword.control.start-block.ruby
    push: maybe-block-parameters
    - match: \{
    scope: punctuation.section.scope.ruby
    push: maybe-block-parameters

    maybe-block-parameters:
    - match: \|
    scope: meta.block.parameters.ruby punctuation.definition.parameters.begin.ruby
    set: block-parameters
    - match: (?=\s*[^\s\|])
    pop: true

    block-parameters:
    - meta_content_scope: meta.block.parameters.ruby
    - match: \|
    scope: meta.block.parameters.ruby punctuation.definition.parameters.end.ruby
    set: try-regex
    - match: '{{identifier}}'
    scope: variable.parameter.ruby
    - match: ','
    scope: punctuation.separator.ruby
    - match: \*
    scope: keyword.operator.splat.ruby
    - match: '&'
    scope: keyword.operator.ruby
    - match: '(?==)'
    set:
    - meta_content_scope: meta.block.parameters.default-value.ruby
    - match: '='
    scope: keyword.operator.assignment.ruby
    set:
    - meta_content_scope: meta.block.parameters.default-value.ruby
    - match: '(?=[,\|])'
    set: block-parameters
    - include: nest-all
    - include: expressions

    keywords:
    - match: '\b(BEGIN|END)\b(?![?!])'
    scope: keyword.control.ruby
    - match: '\b(class|module)\b(?![?!])'
    scope: keyword.control.ruby
    - match: '\b(begin|end|ensure|rescue)\b(?![?!])'
    scope: keyword.control.ruby
    - match: '\b(case|else|ensure|for|in|then)\b(?![?!])'
    scope: keyword.control.ruby
    - match: '\b(elsif|if|unless|when|while|until)\b(?![?!])'
    scope: keyword.control.ruby
    push: after-keyword
    - match: \b(and|not|or)\b
    scope: keyword.operator.logical.ruby
    push: after-keyword
    - match: '!+|&&|\|\||\^'
    scope: keyword.operator.logical.ruby
    push: after-operator
    - match: '\b(alias|alias_method|break|next|redo|retry|return|super|undef|yield)\b(?![?!])|\bdefined\?|\bblock_given\?'
    scope: keyword.control.pseudo-method.ruby

    operators:
    - match: '=>'
    scope: punctuation.separator.key-value.ruby
    push: after-operator
    - match: '<<=|%=|&=|\*=|\*\*=|\+=|\-=|\^=|\|{1,2}=|<<'
    scope: keyword.operator.assignment.augmented.ruby
    push: after-operator
    - match: '<=>|<(?!<|=)|>(?!<|=|>)|<=|>=|===|==|=~|!=|!~'
    scope: keyword.operator.comparison.ruby
    push: after-operator
    - match: (%|&|\*\*|\*|\+|\-|/)
    scope: keyword.operator.arithmetic.ruby
    push: after-operator
    - match: '='
    scope: keyword.operator.assignment.ruby
    push: after-operator
    - match: \||~|>>
    scope: keyword.operator.other.ruby
    push: after-operator
    - match: \?
    scope: keyword.operator.conditional.ruby
    push:
    # Handle hash-key-lookalike of identifier: in ternary
    - match: '\s*{{identifier}}(:)(?!:)'
    captures:
    1: keyword.operator.conditional.ruby
    - include: after-operator
    - match: ':(?!:)'
    scope: keyword.operator.conditional.ruby
    push: after-operator
    - match: \;
    scope: punctuation.terminator.statement.ruby
    push: after-operator
    - match: ","
    scope: punctuation.separator.ruby
    push: after-operator
    - match: '\['
    scope: punctuation.section.array.ruby
    push: after-operator
    - match: \(
    scope: punctuation.definition.group.begin.ruby
    push: after-operator
    # Opening { is handled by "block" context to try and detect parameters
    - match: '\}'
    scope: punctuation.section.scope.ruby
    - match: '\]'
    scope: punctuation.section.array.ruby
    - match: \)
    scope: punctuation.definition.group.end.ruby
    - match: '\.\.\.?'
    scope: keyword.operator.ruby
    push: after-operator

    identifiers-accessors:
    # This consumes class/module access to prevent issues parsing : as part
    # of a ternary operator
    - match: '(::)(?={{identifier}}{{method_punctuation}})'
    scope: punctuation.accessor.ruby
    push:
    - include: well-known-methods
    - match: '{{identifier}}{{method_punctuation}}'
    - match: ''
    set: after-identifier
    # This consumes attribute access so we don't need a lookbehind for .
    - match: '(\.)(?={{identifier}}{{method_punctuation}})'
    scope: punctuation.accessor.ruby
    push:
    - include: well-known-methods
    - match: '{{identifier}}{{method_punctuation}}'
    - match: ''
    set: after-identifier
    # This consumes method names ending in punctuation so we don't need a lookbehind for ?, ! or =
    - match: '{{identifier}}{{method_punctuation}}'
    # This consumes module/class accessor so we don't need a lookbehind for ::
    push: after-identifier
    - match: '::|\.'
    scope: punctuation.accessor.ruby

    after-identifier:
    # Handles a : right after an identifier. In this case it can't be the
    # beginning of a symbol, so it must be part of a ternary operator
    - match: ':(?!:)'
    scope: keyword.operator.conditional.ruby
    pop: true
    - match: ''
    pop: true

    variables:
    - match: '(@)[a-zA-Z_]\w*'
    scope: variable.other.readwrite.instance.ruby
    captures:
    1: punctuation.definition.variable.ruby
    - match: '(@@)[a-zA-Z_]\w*'
    scope: variable.other.readwrite.class.ruby
    captures:
    1: punctuation.definition.variable.ruby
    - match: '(\$)[a-zA-Z_]\w*'
    scope: variable.other.readwrite.global.ruby
    captures:
    1: punctuation.definition.variable.ruby
    - match: '(\$)(!|@|&|`|''|\+|\d+|~|=|/|\\|,|;|\.|<|>|_|\*|\$|\?|:|"|-[0adFiIlpv])'
    scope: variable.other.readwrite.global.pre-defined.ruby
    captures:
    1: punctuation.definition.variable.ruby
    - match: '\b(ENV)\['
    captures:
    1: variable.other.constant.ruby
    push:
    - meta_scope: meta.environment-variable.ruby
    - match: '\]'
    pop: true
    - include: expressions
    - match: '(::)?(\b[[:upper:]]\w*)(?=((\.|::)[[:alpha:]_]|\[))'
    captures:
    1: punctuation.accessor.ruby
    2: support.class.ruby
    - match: '\b[[:upper:]]\w*\b'
    scope: variable.other.constant.ruby

    well-known-methods:
    - match: '\b(initialize|new|loop|include|extend|prepend|raise|fail|attr_reader|attr_writer|attr_accessor|attr|catch|throw|module_function|public|protected|private)\b(?![?!])'
    scope: keyword.other.special-method.ruby
    - match: \b(require|require_relative|gem)\b
    captures:
    1: keyword.other.special-method.ruby
    push:
    - meta_scope: meta.require.ruby
    - match: $|(?=#)
    captures:
    1: keyword.other.special-method.ruby
    pop: true
    - include: expressions
    # Conversion methods
    - match: |-
    (?x:
    \b(
    to_ary|
    to_a|
    to_c|
    to_enum|
    to_f|
    to_hash|
    to_h|
    to_int|
    to_io|
    to_i|
    to_proc|
    to_r|
    to_str|
    to_sym|
    to_s
    )\b
    (?![!?=])
    )
    scope: support.function.builtin.ruby
    # Methods that may be followed by a regex
    - match: |-
    (?x:
    \b(
    gsub!|
    sub!
    )(?=\s)
    |
    \b(
    assert_match|
    assert_no_match|
    gsub|
    index|
    match|
    scan|
    sub
    )\b
    )
    scope: support.function.builtin.ruby
    push: try-regex
    # Methods from the Object class not handled elsewhere, ending in punctuation
    - match: |-
    (?x:
    \b(
    eql\?|
    instance_of\?|
    instance_variable_defined\?|
    is_a\?|
    kind_of\?|
    nil\?|
    respond_to\?|
    respond_to_missing\?|
    tainted\?|
    untrusted\?
    )
    )
    scope: support.function.builtin.ruby
    # Methods from the Object class not handled elsewhere
    - match: |-
    (?x:
    \b(
    class|
    clone|
    define_singleton_method|
    display|
    dup|
    enum_for|
    extend|
    freeze|
    frozen?|
    hash|
    inspect|
    instance_variable_get|
    instance_variable_set|
    instance_variables|
    itself|
    method|
    methods|
    object_id|
    private_methods|
    protected_methods|
    public_method|
    public_methods|
    public_send|
    remove_instance_variable|
    send|
    singleton_class|
    singleton_method|
    singleton_methods|
    taint|
    tap|
    trust|
    untaint|
    untrust
    )\b
    (?![!?=])
    )
    scope: support.function.builtin.ruby
    # Methods from the Kernel class not handled elsewhere, ending in punctuation
    - match: |-
    (?x:
    \b(
    autoload\?|
    iterator\?|
    exit!
    )
    )
    scope: support.function.builtin.ruby
    # Methods from the Kernel class not handled elsewhere
    - match: |-
    (?x:
    \b(
    Array|
    Complex|
    Float|
    Hash|
    Integer|
    Rational|
    String|
    __callee__|
    __dir__|
    __method__|
    abort|
    at_exit|
    autoload|
    binding|
    callcc|
    caller|
    caller_locations|
    chomp|
    chop|
    eval|
    exec|
    exit|
    fork|
    format|
    gets|
    global_variables|
    gsub|
    lambda|
    load|
    local_variables|
    open|
    p|
    print|
    printf|
    proc|
    putc|
    puts|
    rand|
    readline|
    readlines|
    require|
    require_relative|
    select|
    set_trace_func|
    sleep|
    spawn|
    sprintf|
    srand|
    sub|
    syscall|
    system|
    test|
    trace_var|
    trap|
    untrace_var|
    warn
    )\b
    (?![!?=])
    )
    scope: support.function.builtin.ruby
    # Methods from the Kernel class not handled elsewhere, ending in punctuation
    - match: |-
    (?x:
    \b(
    class_variable_defined\?|
    const_defined\?|
    include\?|
    instance_methods\?|
    method_defined\?|
    private_method_defined\?|
    protected_method_defined\?|
    public_method_defined\?|
    singleton_class\?
    )
    )
    scope: support.function.builtin.ruby
    # Methods from the Module class not handled elsewhere
    - match: |-
    (?x:
    \b(
    ancestors|
    append_features|
    class_eval|
    class_exec|
    class_variable_get|
    class_variable_set|
    class_variables|
    const_get|
    const_missing|
    const_set|
    constants|
    define_method|
    extend_object|
    extended|
    freeze|
    included|
    included_modules|
    inspect|
    method_added|
    method_removed|
    method_undefined|
    module_eval|
    module_exec|
    name|
    prepend_features|
    prepended|
    private_class_method|
    private_constant|
    private_instance_methods|
    protected_instance_methods|
    public_class_method|
    public_constant|
    public_instance_method|
    public_instance_methods|
    refine|
    remove_class_variable|
    remove_const|
    remove_method|
    undef_method|
    using
    )\b
    (?![!?=])
    )
    scope: support.function.builtin.ruby
    method:
    - match: \b(def)\b
    scope: meta.function.ruby keyword.control.def.ruby
    push:
    - meta_content_scope: meta.function.ruby
    - match: '(self)(\.)({{identifier}}{{method_punctuation}}|===?|>[>=]?|<=>|<[<=]?|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\]=?)'
    captures:
    1: variable.language.ruby
    2: punctuation.accessor.ruby
    3: entity.name.function.ruby
    set: method-parameters-start
    - match: '===?|>[>=]?|<=>|<[<=]?|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\]=?'
    scope: entity.name.function.ruby
    set: method-parameters-start
    - match: '(?:({{identifier}})(::|\.))?{{identifier}}{{method_punctuation}}'
    scope: entity.name.function.ruby
    captures:
    1: support.other.namespace.ruby
    2: punctuation.accessor.ruby
    set: method-parameters-start
    - match: '$'
    pop: true
    - match: '(?=\S)'
    pop: true

    method-parameters-start:
    - meta_content_scope: meta.function.ruby
    - match: '(?=\()'
    set:
    - meta_content_scope: meta.function.parameters.ruby
    - match: '\('
    scope: punctuation.definition.group.begin.ruby
    set: method-parameters
    # No parameters
    - match: '(?=$|;|#)'
    pop: true
    # No parentheses around parameters
    - match: '(?=[[:alpha:]_*])'
    set: method-bare-parameters

    method-parameters:
    - meta_content_scope: meta.function.parameters.ruby
    - match: '\)'
    scope: meta.function.parameters.ruby punctuation.definition.group.end.ruby
    pop: true
    - match: '{{identifier}}'
    scope: variable.parameter.ruby
    - include: comments
    - match: ','
    scope: punctuation.separator.ruby
    - match: '\*'
    scope: keyword.operator.splat.ruby
    - match: '&'
    scope: keyword.operator.ruby
    # De-structuring
    - match: \(
    scope: punctuation.definition.group.begin.ruby
    push:
    - match: \)
    scope: punctuation.definition.group.end.ruby
    pop: true
    - match: '{{identifier}}'
    scope: variable.parameter.ruby
    - match: ','
    scope: punctuation.separator.ruby
    - match: '\*'
    scope: keyword.operator.splat.ruby
    # Default values
    - match: (?==)
    set:
    - meta_content_scope: meta.function.parameters.default-value.ruby
    - match: '='
    scope: keyword.operator.assignment.ruby
    set:
    - meta_content_scope: meta.function.parameters.default-value.ruby
    - match: '(?=[,\)])'
    set: method-parameters
    - include: nest-all
    - include: expressions
    # Keyword parameter (with default value support)
    - match: (?=:)
    set:
    - meta_content_scope: meta.function.parameters.default-value.ruby
    - match: ':'
    scope: punctuation.separator.ruby
    set:
    - meta_content_scope: meta.function.parameters.default-value.ruby
    - match: '(?=[,\)])'
    set: method-parameters
    - include: nest-all
    - include: expressions

    # When no parentheses are placed around the parameters
    method-bare-parameters:
    - meta_content_scope: meta.function.parameters.ruby
    - match: '(?=$|;|#)'
    pop: true
    - match: '{{identifier}}'
    scope: variable.parameter.ruby
    - match: ','
    scope: punctuation.separator.ruby
    - match: '\*'
    scope: keyword.operator.splat.ruby
    - match: '&'
    scope: keyword.operator.ruby
    # Default values
    - match: (?==)
    set:
    - meta_content_scope: meta.function.parameters.default-value.ruby
    - match: '='
    scope: punctuation.operator.assignment.ruby
    set:
    - meta_content_scope: meta.function.parameters.default-value.ruby
    - match: '(?=$|[,;])'
    set: method-bare-parameters
    - include: nest-all
    - include: expressions
    # Keyword parameter (with default value support)
    - match: (?=:)
    set:
    - meta_content_scope: meta.function.parameters.default-value.ruby
    - match: ':'
    scope: punctuation.separator.ruby
    set:
    - meta_content_scope: meta.function.parameters.default-value.ruby
    - match: '(?=$|[,;])'
    set: method-bare-parameters
    - include: nest-all
    - include: expressions

    strings:
    - include: early-strings
    - include: regexes
    - include: late-strings

    early-strings:
    # single quoted string (does not allow interpolation)
    - match: "'"
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.single.ruby
    - match: "'"
    scope: punctuation.definition.string.end.ruby
    pop: true
    - match: \\'|\\\\
    scope: constant.character.escape.ruby
    - include: string-placeholder
    # double quoted string (allows for interpolation)
    - match: '"'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.double.ruby
    - match: '"'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: string-placeholder
    # execute string (allows for interpolation)
    - match: "`"
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.interpolated.ruby
    - match: "`"
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    # execute string (allow for interpolation)
    - match: '%x\{'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.interpolated.ruby
    - match: '\}'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-curly-i
    # execute string (allow for interpolation)
    - match: '%x\['
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.interpolated.ruby
    - match: '\]'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-brackets-i
    # execute string (allow for interpolation)
    - match: '%x\<'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.interpolated.ruby
    - match: \>
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-ltgt-i
    # execute string (allow for interpolation)
    - match: '%x\('
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.interpolated.ruby
    - match: \)
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-parens-i
    # execute string (allow for interpolation)
    - match: '%x([^\w])'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.interpolated.ruby
    - match: \1
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char

    late-strings:
    # literal capable of interpolation ()
    - match: '%[QWI]?\('
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.upper.ruby
    - match: \)
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-parens-i
    # "literal capable of interpolation []"
    - match: '%[QWI]?\['
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.upper.ruby
    - match: '\]'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-brackets-i
    # literal capable of interpolation <>
    - match: '%[QWI]?\<'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.upper.ruby
    - match: \>
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-ltgt-i
    # literal capable of interpolation -- {}
    - match: '%[QWI]?\{'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.double.ruby.mod
    - match: '\}'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-curly-i
    # literal capable of interpolation -- wildcard
    - match: '%[QWI]([^\w])'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.upper.ruby
    - match: \1
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    # literal capable of interpolation -- wildcard
    - match: '%([^\w\s=])'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.other.ruby
    - match: \1
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    # literal incapable of interpolation -- ()
    - match: '%[qwsi]\('
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.lower.ruby
    - match: \)
    scope: punctuation.definition.string.end.ruby
    pop: true
    - match: \\\)|\\\\
    scope: constant.character.escape.ruby
    - include: nest-parens
    # literal incapable of interpolation -- <>
    - match: '%[qwsi]\<'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.lower.ruby
    - match: \>
    scope: punctuation.definition.string.end.ruby
    pop: true
    - match: \\\>|\\\\
    scope: constant.character.escape.ruby
    - include: nest-ltgt
    # literal incapable of interpolation -- []
    - match: '%[qwsi]\['
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.lower.ruby
    - match: '\]'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - match: '\\\]|\\\\'
    scope: constant.character.escape.ruby
    - include: nest-brackets
    # literal incapable of interpolation -- {}
    - match: '%[qwsi]\{'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.lower.ruby
    - match: '\}'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - match: '\\\}|\\\\'
    scope: constant.character.escape.ruby
    - include: nest-curly
    # literal incapable of interpolation -- wildcard
    - match: '%[qwsi]([^\w])'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.quoted.other.literal.lower.ruby
    - match: \1
    scope: punctuation.definition.string.end.ruby
    pop: true
    # Cant be named because its not necessarily an escape
    - match: \\.

    after-keyword:
    - include: try-regex

    after-operator:
    - include: try-regex

    try-regex:
    # Generally for multiline regexes, one of the %r forms below will be used,
    # so we bail out if we can't find a second / on the current line
    - match: '\s*(/)(?![*+{}?])(?=.*/)'
    captures:
    1: string.regexp.classic.ruby punctuation.definition.string.ruby
    push:
    - meta_content_scope: string.regexp.classic.ruby
    - match: "(/)([eimnosux]*)"
    scope: string.regexp.classic.ruby
    captures:
    1: punctuation.definition.string.ruby
    2: keyword.other.ruby
    pop: true
    - include: regex-sub
    - match: ''
    pop: true

    regexes:
    # Needs higher precedence than regular expressions.
    - match: /=
    scope: keyword.operator.assignment.augmented.ruby
    - match: '(?=^\s*/)'
    push: try-regex
    - match: (?=/\s*[^\w\(\s@"'])
    push: try-regex
    # regular expressions (literal)
    - match: '%r\{'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.regexp.mod-r.ruby
    - match: '\}[eimnosux]*'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: regex-sub
    - include: nest-curly-r
    # regular expressions (literal)
    - match: '%r\['
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.regexp.mod-r.ruby
    - match: '\][eimnosux]*'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: regex-sub
    - include: nest-brackets-r
    # regular expressions (literal)
    - match: '%r\('
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.regexp.mod-r.ruby
    - match: '\)[eimnosux]*'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: regex-sub
    - include: nest-parens-r
    # regular expressions (literal)
    - match: '%r\<'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.regexp.mod-r.ruby
    - match: '\>[eimnosux]*'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: regex-sub
    - include: nest-ltgt-r
    # regular expressions (literal)
    - match: '%r([^\w])'
    scope: punctuation.definition.string.begin.ruby
    push:
    - meta_scope: string.regexp.mod-r.ruby
    - match: '\1[eimnosux]*'
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: regex-sub

    regex-sub:
    - include: interpolated-ruby
    - include: escaped-char
    - match: '(\{)\d+(,\d+)?(\})'
    scope: string.regexp.arbitrary-repetition.ruby
    captures:
    1: punctuation.definition.arbitrary-repetition.ruby
    3: punctuation.definition.arbitrary-repetition.ruby
    - match: '\[(?:\^?\])?'
    scope: punctuation.definition.character-class.ruby
    push:
    - meta_scope: string.regexp.character-class.ruby
    - match: '\]'
    scope: punctuation.definition.character-class.ruby
    pop: true
    - include: escaped-char
    - match: \(
    scope: punctuation.definition.group.ruby
    push:
    - meta_scope: string.regexp.group.ruby
    - match: \)
    scope: punctuation.definition.group.ruby
    pop: true
    - include: regex-sub
    # We are restrictive in what we allow to go after the comment character to
    # avoid false positives, since the availability of comments depend on regexp
    # flags.
    - match: '(?:^|\s)(#)\s[[a-zA-Z0-9,. \t?!-][^\x{00}-\x{7F}]]*$'
    scope: comment.line.number-sign.ruby
    captures:
    1: punctuation.definition.comment.ruby

    nest-brackets-r:
    - match: '\['
    scope: punctuation.section.scope.ruby
    push:
    - match: '\]'
    scope: punctuation.section.scope.ruby
    pop: true
    - include: regex-sub
    - include: nest-brackets-r
    nest-curly-r:
    - match: '\{'
    scope: punctuation.section.scope.ruby
    push:
    - match: '\}'
    scope: punctuation.section.scope.ruby
    pop: true
    - include: regex-sub
    - include: nest-curly-r
    nest-ltgt-r:
    - match: \<
    scope: punctuation.section.scope.ruby
    push:
    - match: \>
    scope: punctuation.section.scope.ruby
    pop: true
    - include: regex-sub
    - include: nest-ltgt-r
    nest-parens-r:
    - match: \(
    scope: punctuation.section.scope.ruby
    push:
    - match: \)
    scope: punctuation.section.scope.ruby
    pop: true
    - include: regex-sub
    - include: nest-parens-r

    nest-brackets:
    - match: '\['
    scope: punctuation.section.scope.ruby
    push:
    - match: '\]'
    scope: punctuation.section.scope.ruby
    pop: true
    - include: nest-brackets
    nest-curly:
    - match: '\{'
    scope: punctuation.section.scope.ruby
    push: [nest-curly-inner, maybe-block-parameters]
    nest-curly-inner:
    - match: '\}'
    scope: punctuation.section.scope.ruby
    pop: true
    - include: nest-curly
    nest-ltgt:
    - match: \<
    scope: punctuation.section.scope.ruby
    push:
    - match: \>
    scope: punctuation.section.scope.ruby
    pop: true
    - include: nest-ltgt
    nest-parens:
    - match: \(
    scope: punctuation.section.scope.ruby
    push:
    - match: \)
    scope: punctuation.section.scope.ruby
    pop: true
    - include: nest-parens

    string-placeholder:
    # %[flags][width][.precision]type
    #
    # A format sequence consists of a percent sign, followed by optional
    # flags, width, and precision indicators, then terminated with a field
    # type character.
    #
    # Also this is used for time format in strftime.
    - match: |-
    (?x)%
    ([#0\- +\*]|(\d+\$))* # flags
    (-?\d+)? # minimum field width
    (\.(\d+)?)? # precision
    [diouxXDOUeEfFgGaAcCsSpnvtTbByYhHmMzZ%] # conversion type
    scope: constant.other.placeholder.ruby
    escaped-char:
    - match: '\\(?:[0-7]{1,3}|x[\da-fA-F]{1,2}|.)'
    scope: constant.character.escape.ruby

    interpolated-ruby:
    - match: '#\{'
    scope: punctuation.section.interpolation.begin.ruby
    push:
    - clear_scopes: 1
    - meta_scope: meta.interpolation
    - meta_content_scope: source.ruby.embedded
    - match: '\}'
    scope: punctuation.section.interpolation.end.ruby
    pop: true
    - include: nest-curly-expressions
    - include: expressions
    - match: '(#@)[[:alpha:]_]\w*'
    scope: variable.other.readwrite.instance.ruby
    captures:
    1: punctuation.definition.variable.ruby
    - match: '(#@@)[[:alpha:]_]\w*'
    scope: variable.other.readwrite.class.ruby
    captures:
    1: punctuation.definition.variable.ruby
    - match: '(#\$)[[:alpha:]_]\w*'
    scope: variable.other.readwrite.global.ruby
    captures:
    1: punctuation.definition.variable.ruby

    nest-curly-expressions:
    - match: '\{'
    scope: punctuation.section.scope.ruby
    push: [nest-curly-expressions-inner, maybe-block-parameters]
    nest-curly-expressions-inner:
    - match: '\}'
    scope: punctuation.section.scope.ruby
    pop: true
    - include: nest-curly-expressions
    - include: expressions

    nest-all:
    - match: '\('
    scope: punctuation.definition.group.begin.ruby
    push:
    - match: '\)'
    scope: punctuation.definition.group.end.ruby
    pop: true
    - include: nest-all
    - include: expressions
    - match: '\{'
    scope: punctuation.section.scope.ruby
    push: [nest-all-inner, maybe-block-parameters]
    - match: '\['
    scope: punctuation.section.array.ruby
    push:
    - match: '\]'
    scope: punctuation.section.array.ruby
    pop: true
    - include: nest-all
    - include: expressions
    nest-all-inner:
    - match: '\}'
    scope: punctuation.section.scope.ruby
    pop: true
    - include: nest-all
    - include: expressions

    nest-brackets-i:
    - match: '\['
    scope: punctuation.section.scope.ruby
    push:
    - match: '\]'
    scope: punctuation.section.scope.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-brackets-i
    nest-curly-i:
    - match: '\{'
    scope: punctuation.section.scope.ruby
    push: [nest-curly-i-inner, maybe-block-parameters]
    nest-curly-i-inner:
    - match: '\}'
    scope: punctuation.section.scope.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-curly-i
    nest-ltgt-i:
    - match: \<
    scope: punctuation.section.scope.ruby
    push:
    - match: \>
    scope: punctuation.section.scope.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-ltgt-i
    nest-parens-i:
    - match: \(
    scope: punctuation.section.scope.ruby
    push:
    - match: \)
    scope: punctuation.section.scope.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: nest-parens-i

    heredocs:
    # heredoc with embedded HTML and indented terminator
    - match: '(<<[-~]"?((?:[_\w]+_|)HTML)\b"?)'
    scope: punctuation.definition.string.begin.ruby
    push: [heredoc-html, trailing-heredoc-start]
    # heredoc with embedded ERB and indented terminator
    - match: '(<<[-~]"?((?:[_\w]+_|)ERB)\b"?)'
    scope: punctuation.definition.string.begin.ruby
    push: [heredoc-erb, trailing-heredoc-start]
    # heredoc with embedded HAML and indented terminator
    - match: '(<<[-~]"?((?:[_\w]+_|)HAML)\b"?)'
    scope: punctuation.definition.string.begin.ruby
    push: [heredoc-haml, trailing-heredoc-start]
    # - match: '(<<[-~]"?((?:[_\w]+_|)ERB)\b"?)'
    # embed: text.html.ruby.custom
    # escape: ^\s*(ERB)$
    # escape_captures:
    # 0: trailing-heredoc
    # heredoc with embedded SQL and indented terminator
    - match: '(<<[-~]"?((?:[_\w]+_|)SQL)\b"?)'
    scope: punctuation.definition.string.begin.ruby
    push: [heredoc-sql, trailing-heredoc-start]
    # heredoc with embedded css and indented terminator
    - match: '(<<[-~]"?((?:[_\w]+_|)CSS)\b"?)'
    scope: punctuation.definition.string.begin.ruby
    push: [heredoc-css, trailing-heredoc-start]
    # heredoc with embedded javascript and indented terminator
    - match: '(<<[-~]"?((?:[_\w]+_|)(?:JS|JAVASCRIPT))\b"?)'
    scope: punctuation.definition.string.begin.ruby
    push: [heredoc-js, trailing-heredoc-start]
    # heredoc with embedded ruby and indented terminator
    - match: '(<<[-~]"?((?:[_\w]+_|)RUBY)\b"?)'
    scope: punctuation.definition.string.begin.ruby
    push: [heredoc-ruby, trailing-heredoc-start]
    # Escaped to prevent recursion?
    # heredoc with embedded shell and indented terminator
    # - match: '(<<[-~]("?)((?:[_\w]+_|)(?:SH|SHELL))\b\1)'
    # scope: punctuation.definition.string.begin.ruby
    # push: [heredoc-shell, trailing-heredoc-start]
    - match: (\=)\s*(<<(\w+))
    captures:
    1: keyword.operator.assignment.ruby
    2: punctuation.definition.string.begin.ruby
    push: [heredoc-assign, trailing-heredoc-no-embedding-start]
    # heredoc with indented terminator
    - match: '(<<[-~](\w+))'
    scope: punctuation.definition.string.begin.ruby
    push: [heredoc-plain, trailing-heredoc-no-embedding-start]

    heredoc-html:
    - meta_scope: string.unquoted.embedded.html.ruby
    - meta_content_scope: text.html.embedded.ruby
    - match: ^\s*\2$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: scope:text.html.basic
    - include: interpolated-ruby
    - include: escaped-char

    heredoc-erb:
    - meta_scope: string.unquoted.embedded.html.ruby
    - meta_content_scope: text.html.ruby.embedded.ruby
    - match: ^\s*\2$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: scope:text.html.ruby
    - include: interpolated-ruby
    - include: escaped-char

    heredoc-haml:
    - meta_scope: string.unquoted.embedded.haml.ruby
    - meta_content_scope: text.haml.embedded.ruby
    - match: ^\s*HAML$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: scope:text.haml
    - include: interpolated-ruby
    - include: escaped-char

    heredoc-sql:
    - meta_scope: string.unquoted.embedded.sql.ruby
    - meta_content_scope: text.sql.embedded.ruby
    - match: ^\s*\2$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: scope:source.sql
    - include: interpolated-ruby
    - include: escaped-char

    heredoc-css:
    - meta_scope: string.unquoted.embedded.css.ruby
    - meta_content_scope: text.css.embedded.ruby
    - match: ^\s*\2$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: 'scope:source.css'
    - include: interpolated-ruby
    - include: escaped-char

    heredoc-js:
    - meta_scope: string.unquoted.embedded.js.ruby
    - meta_content_scope: text.js.embedded.ruby
    - match: ^\s*\2$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: 'scope:source.js'
    - include: interpolated-ruby
    - include: escaped-char

    heredoc-ruby:
    - meta_scope: string.unquoted.embedded.ruby.ruby
    - meta_content_scope: text.ruby.embedded.ruby
    - match: ^\s*\2$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char
    - include: expressions

    #heredoc-shell:
    # - meta_scope: string.unquoted.embedded.shell.ruby
    # - meta_content_scope: text.shell.embedded.ruby
    # - match: ^\s*\2$
    # scope: punctuation.definition.string.end.ruby
    # pop: true
    # - include: Shell-Unix-Generic.sublime-syntax
    # - include: interpolated-ruby
    # - include: escaped-char

    # This prevents clear_scopes from applying to the push token
    trailing-heredoc-start:
    - match: ''
    set: trailing-heredoc

    trailing-heredoc:
    - clear_scopes: 2
    - match: '$'
    pop: true
    - include: expressions

    heredoc-assign:
    - meta_scope: string.unquoted.heredoc.ruby
    - match: ^\s*\3$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char

    heredoc-plain:
    - meta_scope: string.unquoted.heredoc.ruby
    - match: ^\s*\2$
    scope: punctuation.definition.string.end.ruby
    pop: true
    - include: interpolated-ruby
    - include: escaped-char

    # This prevents clear_scopes from applying to the push token
    trailing-heredoc-no-embedding-start:
    - match: ''
    set: trailing-heredoc-no-embedding

    trailing-heredoc-no-embedding:
    - clear_scopes: 1
    - match: '$'
    pop: true
    - include: expressions

    data-section:
    - match: ^__END__\n
    scope: string.unquoted.program-block.ruby
    push:
    - meta_content_scope: text.plain
    - match: (?=<?xml|<(?i:html\b)|!DOCTYPE (?i:html\b))
    push:
    - meta_scope: text.html.embedded.ruby
    - include: scope:text.html.basic