Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1504 lines (1447 sloc) 44.8 KB
%YAML 1.2
---
name: Ruby
# 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
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 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-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