From 1ab573822a00dbf17e0b3d24fb3fc1a78d528282 Mon Sep 17 00:00:00 2001 From: Toshimaru Date: Sat, 8 Aug 2026 09:56:00 +0900 Subject: [PATCH 1/3] Merge singleton and plain def branches in ruby.lsh The two forms differed only by an optional `\w+\.` receiver prefix, so they collapse into one regex with fewer redundant "def" prefix tests on the common path. --- crates/lsh/definitions/ruby.lsh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/lsh/definitions/ruby.lsh b/crates/lsh/definitions/ruby.lsh index ddc3973f01c..e10dcbcf39b 100644 --- a/crates/lsh/definitions/ruby.lsh +++ b/crates/lsh/definitions/ruby.lsh @@ -39,11 +39,8 @@ pub fn ruby() { } } 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+[!?=]?)/ { + } else if /def\s+(?:\w+\.)?(\w+[!?=]?)/ { + // The optional prefix covers singleton methods, as in `def self.foo`. 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)\>/ { From 2483ca8162925087b0f363bf4b4520986a7959e2 Mon Sep 17 00:00:00 2001 From: Toshimaru Date: Sat, 8 Aug 2026 10:07:16 +0900 Subject: [PATCH 2/3] Merge float and integer alternatives in ruby.lsh numeric literal regex The exponent suffix (?:e[+-]?[\d_]+)? was duplicated across separate float and integer alternatives. Making the fractional part optional instead expresses the same constraint with one shared exponent clause. --- crates/lsh/definitions/ruby.lsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lsh/definitions/ruby.lsh b/crates/lsh/definitions/ruby.lsh index e10dcbcf39b..c640b4f448b 100644 --- a/crates/lsh/definitions/ruby.lsh +++ b/crates/lsh/definitions/ruby.lsh @@ -47,7 +47,7 @@ pub fn ruby() { 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?)/ { + } else if /(?i:-?(?:0x[\da-f_]+|0b[01_]+|0o[0-7_]+|[\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+/ { From a802f632b97106a143f8d0c5f14c8f34a7c19150 Mon Sep 17 00:00:00 2001 From: Toshimaru Date: Sat, 8 Aug 2026 10:12:05 +0900 Subject: [PATCH 3/3] Highlight #{...} interpolation in ruby.lsh strings Inline the double-quote and backtick string loops so an interpolation branch can highlight #{...} as variable, matching how shellscript.lsh handles $var. Nested braces aren't supported, matching the same limitation there. --- assets/highlighting-tests/ruby.rb | 2 ++ crates/lsh/definitions/ruby.lsh | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/assets/highlighting-tests/ruby.rb b/assets/highlighting-tests/ruby.rb index d8e4cc25af2..01fe78c29bb 100644 --- a/assets/highlighting-tests/ruby.rb +++ b/assets/highlighting-tests/ruby.rb @@ -39,7 +39,9 @@ 'single quotes with escape: \' \n \t \\' "double quotes with escape: \" \n \t \\" "interpolated: #{1 + 1} and a # that is not a comment" +"escaped \#{1 + 1} is not interpolated" `echo shell command` +`echo #{Time.now}` # Symbols and variables :symbol diff --git a/crates/lsh/definitions/ruby.lsh b/crates/lsh/definitions/ruby.lsh index c640b4f448b..37b95e95c38 100644 --- a/crates/lsh/definitions/ruby.lsh +++ b/crates/lsh/definitions/ruby.lsh @@ -27,11 +27,25 @@ pub fn ruby() { } else if /'/ { single_quote_string(); } else if /"/ { - double_quote_string(); + until /$/ { + yield string; + if /\\./ { + // Escape sequences. `\#{` escapes interpolation, so this must come first. + } else if /#\{[^}]*\}/ { + // Interpolation. Nested braces, as in `#{ {a: 1} }`, are not supported. + yield variable; + } else if /"/ { + yield string; + break; + } + } } else if /`/ { until /$/ { + yield string; if /\\./ { // Escape sequences + } else if /#\{[^}]*\}/ { + yield variable; } else if /`/ { yield string; break;