Skip to content

fix(ruby): properly highlight %r{} regex literals containing nested patterns #4286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

j-erasmus
Copy link

@j-erasmus j-erasmus commented Jun 27, 2025

Summary

This fixes an issue where %r{}-style regular expressions in Ruby were not correctly highlighted when they contained nested patterns or complex escape sequences. Patterns like (\.{2}|\A/) would either break highlighting or be ignored entirely due to improper handling of nesting inside %r{}.

Resolves: #4281


Root Cause

Highlight.js previously treated %r{} regex literals as flat strings. This caused the parser to:

  • Fail on nested constructs like {2} (quantifiers)
  • Break on position anchors like \A
  • Terminate the regex early or skip highlighting entirely

Fix

  • Introduces a top-level REGEXP mode to match %r{}, %r(), %r[], %r!, etc. anywhere in Ruby code
  • Enables nested brace parsing inside %r{} using contains: ['self']
  • Removes %r variants from the legacy RE_STARTERS_RE block (which still handles /.../ contexts)
  • Keeps other regex behavior intact and scoped

Screenshots

Before After
Screenshot 2025-06-27 at 10 39 06 Screenshot 2025-06-27 at 10 39 24

Previously, the Ruby grammar only recognized %r{} regex literals
in limited contexts, causing patterns like %r{(\.{2}|\A/)} to
break syntax highlighting.

This fix:
- Adds a top-level REGEXP mode to support all %r{} variations globally
- Enables nested brace handling inside %r{...}
- Removes redundant %r handling tied to RE_STARTERS_RE
- Adds a test case to regexes.txt to prevent regression
Comment on lines 244 to 247
{ begin: '%r\\(', end: '\\)[a-z]*' },
{ begin: '%r!', end: '![a-z]*' },
{ begin: '%r\\[', end: '\\][a-z]*' },
{ begin: '/', end: '/[a-z]*' }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these should be regex literals, not strings.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshgoebel fixed 👍

hljs.BACKSLASH_ESCAPE,
SUBST,
{
begin: /\{/, end: /\}/,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about a { or } inside of a character group []... please adds some tests and show that this would also be properly handled... I worry this is going to get very hard to get right without a full regex parser.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just handling [] with escapes inside (to allow escaping a other closing ]) would be sufficient though?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshgoebel this should now be fixed, I also added some tests to verify these cases.

@Yang062
Copy link

Yang062 commented Jul 4, 2025 via email

- Add character class handling to prevent {} inside [] from being treated as interpolation
- Support escaped closing brackets (\]) within character classes
- Ensure quantifiers like {3} still work outside character classes
- Add test cases to verify edge cases are handled properly
@j-erasmus j-erasmus force-pushed the 466023-fix-ruby-syntax-highlighting branch from eb40cc7 to 10e9c97 Compare July 7, 2025 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(ruby) Syntax highlight fails with %r/ Ruby expression
3 participants