Subject: bug#79454: ruby-mode regex syntax highlighting incorrect

From: Dmitry Gutov <[email-masked]>
Date: Tue, 16 Sep 2025 02:21:17 +0300

Hi!

On 15/09/2025 22:47, nathan wrote:
> 
> When editing a ruby file I did an assignment like this:
> ```ruby
> some_var = 10
> some_var /= 2 # some_var is now equal to 5
> ```

Thanks for the report. Somehow, this didn't come up yet.

> This is a valid operator however the highlighting showed this as being
> part of a regex string which can be formed like this:
> ```ruby
> test_str = 'this is a test string'
> test_str.match? /test/ # Returns true
> ```
> 
> Here are the cases that I tested. The first col is what I did, the
> second is what I expected and the third is how it was highlighted:
> 
> expression # expectation # reality
> 12/3       # division    # division
> 12 /3      # division    # regex
> 12/ 3      # division    # division
> 12 / 3     # division    # division

Ruby is a little weird. For instance, your second example

   12 /3

is parsed as expected, but

   a /3

will be parsed like /3 is the beginning of a regexp, apparently because 
'a' could be a method that accepts a regexp.

And the operator /= is an exception.

We can make an effort to parse numbers literals followed by division 
specially as well, but TBF that comes up quite rarely in real-life code.

> I am not an expert in Ruby syntax but I would imagine that the change
> would be something like: require two slashes on the same line to
> highlight it as a regex expression

Ruby supports regexp spanning multiple lines, so that's not exactly an 
option.

I've pushed a tweak that creates an exception for '/=', hope it resolves 
the important part of the issue (commit 76f50fa55ce19eb4):

diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el
index 459f8f338f7..b1104bf88a0 100644
--- a/lisp/progmodes/ruby-mode.el
+++ b/lisp/progmodes/ruby-mode.el
@@ -2204,6 +2204,7 @@ ruby-syntax-propertize
                          (or (not
                               ;; Looks like division.
                               (or (eql (char-after) ?\s)
+                                 (eql (char-after) ?=)
                                   (not (eql (char-before (1- (point))) 
?\s))))
                              (save-excursion
                                (forward-char -1)