diff --git a/assets/highlighting-tests/ruby.rb b/assets/highlighting-tests/ruby.rb new file mode 100644 index 00000000000..d8e4cc25af2 --- /dev/null +++ b/assets/highlighting-tests/ruby.rb @@ -0,0 +1,133 @@ +# Single-line comment + +=begin +Multi-line comment +=end + +=begin rdoc +The rest of the `=begin` line is ignored by Ruby. +=end + +# Numbers +42 +3.14 +0.5 +1e10 +1.5e-3 +0xff +0xFF +0b1010 +0o77 +1_000_000 +3.14r +2i +1.upto(3) +(1..3).to_a +(1...3).to_a + +# Constants +true +false +nil +Object +String +Demo::Animal +::Kernel +MAX_SIZE = 10 + +# Strings +'single quotes with escape: \' \n \t \\' +"double quotes with escape: \" \n \t \\" +"interpolated: #{1 + 1} and a # that is not a comment" +`echo shell command` + +# Symbols and variables +:symbol +:method_name? +attr_accessor :name, :age +@instance_var +@@class_var +$global_var +{ key: 'value' } + +# Control flow keywords +if true + puts "yes" +elsif false + puts "no" +else + puts "maybe" +end + +unless nil + puts "not nil" +end + +case 42 +when 1 + puts "one" +else + puts "other" +end + +for i in 1..3 + next if i == 2 + break if i == 3 +end + +while false + redo +end + +until true + puts "spin" +end + +begin + raise "oops" +rescue StandardError => e + retry +ensure + puts e +end + +puts "even" if 42.even? and not false +puts "odd" or true + +# Definitions and method calls +module Demo + class Animal + def initialize(name) + @name = name + end + + def self.create(name) + new(name) + end + + def ==(other) + @name == other.name + end + + def name=(value) + @name = value + end + + def speak! = puts "#{@name} speaks" + end +end + +alias old_speak speak! +undef old_speak + +BEGIN { puts "start" } +END { puts "finish" } +defined? Demo +puts __FILE__, __LINE__ +self +super +yield + +puts "hello" +Array.new(3) +greet("world") diff --git a/crates/lsh/definitions/ruby.lsh b/crates/lsh/definitions/ruby.lsh new file mode 100644 index 00000000000..ddc3973f01c --- /dev/null +++ b/crates/lsh/definitions/ruby.lsh @@ -0,0 +1,77 @@ +#[display_name = "Ruby"] +#[path = "**/*.gemspec"] +#[path = "**/*.rake"] +#[path = "**/*.rb"] +#[path = "**/*.ru"] +#[path = "**/Gemfile"] +#[path = "**/Rakefile"] +pub fn ruby() { + if /=begin\>.*/ { + loop { + yield comment; + await input; + if /=end\>.*/ { + yield comment; + break; + } + if /.*/ {} + } + return; + } + + until /$/ { + yield other; + + if /#.*/ { + yield comment; + } else if /'/ { + single_quote_string(); + } else if /"/ { + double_quote_string(); + } else if /`/ { + until /$/ { + if /\\./ { + // Escape sequences + } else if /`/ { + yield string; + break; + } + } + } else if /(?:begin|break|case|do|elsif|else|end|ensure|for|if|in|next|redo|rescue|retry|return|then|unless|until|when|while)\>/ { + yield keyword.control; + } else if /def\s+\w+\.(\w+[!?=]?)/ { + // Singleton method, as in `def self.foo`. + yield keyword.other; + yield $1 as method; + } else if /def\s+(\w+[!?=]?)/ { + yield keyword.other; + yield $1 as method; + } else if /(?:BEGIN|END|__ENCODING__|__FILE__|__LINE__|alias|and|class|defined\?|def|module|not|or|self|super|undef|yield)\>/ { + yield keyword.other; + } else if /(?:true|false|nil)\>/ { + yield constant.language; + } else if /(?i:-?(?:0x[\da-f_]+|0b[01_]+|0o[0-7_]+|[\d_]+\.[\d_]+(?:e[+-]?[\d_]+)?|[\d_]+(?:e[+-]?[\d_]+)?)r?i?)/ { + // Floats need digits on both sides of the dot, so that `1..2` and + // `1.upto(2)` don't swallow the dot. + if /\w+/ { + // Invalid numeric literal + } else { + yield constant.numeric; + } + } else if /(?:@{1,2}|\$)[A-Za-z_]\w*/ { + yield variable; + } else if /::/ { + // Scope resolution, so that `Foo::Bar` isn't mistaken for a symbol. + } else if /:[A-Za-z_]\w*[!?=]?/ { + yield constant.language; + } else if /[A-Z]\w*/ { + yield storage.type; + } else if /(\w+[!?=]?)\s*\(/ { + yield $1 as method; + } else if /\w+[!?=]?/ { + // Gobble word chars to align the next iteration on a word boundary. + } + + yield other; + } +}