Skip to content

Conversation

@tompng
Copy link
Member

@tompng tompng commented Apr 11, 2025

#1024
Migrate syntax highlight from Ripper to Prism

Syntax highlighting

Colorize from these three information:

  • Basically colorize from Prism token types
  • Error coloring from error location
  • Exceptional case from Prism syntax tree

Mix tokens and pseudo tokens created from error location and syntax tree which is prior than normal tokens.

Exceptional case

Symbol name can by IDENTIFIER, operator-token or keyword-token. It may not be placed after SYMBOL_BEGIN

:symbol; :<=>; :if

<<A; :\
A
symbol

Symbol and String colorize
Sometimes it's hard to distinguish symbol and string because both consists of same token types: STRING_CONTENT, EMBEXPR_BEGIN, EMBEXPR_END and STRING_END.
Traversing syntax tree is the easiest way to colorize it correctly.

%w[a b c]
%i[a b c]
:"symbol#{embed}symbol"
"string#{embed}string"

IRB colors defined method name blue. Doing this is really easy from syntax tree but hard from token.

def method_name_colored_blue_in_irb; end

@tompng tompng force-pushed the ripper_to_prism_colorize branch 3 times, most recently from 5e7d118 to b6e85df Compare January 16, 2026 15:32
def test_context_mode_ruby_box
omit if RUBY_VERSION < "4.0.0"
omit if RUBY_VERSION < '4.0.0'
pend if Prism::VERSION == '1.0.0' # Combination of ruby-4.0.0's Ruby::Box and prism-1.0.0 crashes
Copy link
Member

Choose a reason for hiding this comment

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

Can we require 1.1.0+ or something like that so we don't need this?

Copy link
Member Author

Choose a reason for hiding this comment

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

I changed it to 1.3.0 which is the one bundled in ruby-3.4.0.
This will also avoid this error

Could not find compatible versions

Because every version of rbs depends on prism >= 1.3.0
  and Gemfile depends on prism = 1.0.0,
  rbs cannot be used.
So, because Gemfile depends on rbs >= 0,
  version solving has failed.

and the workaround I added in Gemfile

if RUBY_VERSION >= "3.0.0" && !is_truffleruby && ENV['PRISM_VERSION'] != '1.0.0'

@tompng tompng force-pushed the ripper_to_prism_colorize branch from b6e85df to 8570345 Compare January 16, 2026 16:20
@tompng tompng marked this pull request as ready for review January 16, 2026 16:20
@tompng tompng force-pushed the ripper_to_prism_colorize branch from 8570345 to 0b59d99 Compare January 16, 2026 16:30
Copy link
Member

@st0012 st0012 left a comment

Choose a reason for hiding this comment

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

This is great. Thank you

@tompng tompng merged commit 309230f into ruby:master Jan 17, 2026
38 of 40 checks passed
@tompng tompng deleted the ripper_to_prism_colorize branch January 17, 2026 06:39
@tompng
Copy link
Member Author

tompng commented Jan 17, 2026

Failed test (truffleruby) seems unrelated to this change. Failed in master too, maybe related to Ripper change.
Most of the failed test will be solved in #1160 (Only one (probably) unrelated failure)

colored = +''
lvars_code = RubyLex.generate_local_variables_assign_code(local_variables)
code_with_lvars = lvars_code ? "#{lvars_code}\n#{code}" : code
result = Prism.parse_lex(code, scopes: [local_variables])

Choose a reason for hiding this comment

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

Prism by default parses code as the latest version it currently supports. In irb you probably want to parse as the version that the user is currently running as.

You can do that by passing in version: RUBY_VERSION (or version: "current" on more recent prism for better error messages)

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good point, I completely forgotten about version option.

In Ruby < 3.3.0, Prism.parse('code', version: :current) fails with

Prism.parse '1+2', version: 'current'
(irb):2:in `parse': invalid version: Requested to parse as `version: 'current'`;  3.2.2 is below the minimum supported syntax. (Prism::CurrentVersionError)

Is there a good way to use 'current', fallback to the nearest supported version?
(It's better to parse with the current version but it might not be such a big problem... maybe.)

Choose a reason for hiding this comment

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

Ah, prism cannot be used to parse ruby syntax below 3.3. I also added "current" only in prism 1.6.0 so I guess you need to bump the requirement in the gemspec.

Apart from bumping the minimum supported ruby version to 3.3 which might be a bit extreme, the best you can do is version: RUBY_VERSION >= "3.3.0" ? "current" : "3.3" I think

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.

3 participants