-
Notifications
You must be signed in to change notification settings - Fork 57
Lexer options don't get passed to lexer #92
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
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Co-authored-by: markets <[email protected]>
Member
|
Please remove the files under |
Co-authored-by: markets <[email protected]>
Co-authored-by: markets <[email protected]>
Contributor
Author
Removed all files under |
markets
approved these changes
Jul 23, 2025
huwd
added a commit
to alphagov/tech-docs-gem
that referenced
this pull request
Jul 31, 2025
Starting around July 29th 2025 tests started failing between ruby 3.1,
3.2 and 3.3 versions.
The two failures were:
```
1) GovukTechDocs::TechDocsHTMLRenderer#render a code block with syntax highlighting sets tab index to 0
Failure/Error: fragment = Nokogiri::HTML::DocumentFragment.parse(super)
NoMethodError:
undefined method `each' for nil:NilClass
opts.each { |k, v| @options[k.to_s] = v }
^^^^^
# ./vendor/bundle/ruby/3.1.0/gems/rouge-3.30.0/lib/rouge/lexer.rb:325:in `initialize'
# ./vendor/bundle/ruby/3.1.0/gems/rouge-3.30.0/lib/rouge/lexer.rb:97:in `new'
# ./vendor/bundle/ruby/3.1.0/gems/rouge-3.30.0/lib/rouge/lexer.rb:97:in `find_fancy'
# ./vendor/bundle/ruby/3.1.0/gems/middleman-syntax-3.6.0/lib/middleman-syntax/highlighter.rb:13:in `highlight'
# ./vendor/bundle/ruby/3.1.0/gems/middleman-syntax-3.6.0/lib/middleman-syntax/redcarpet_code_renderer.rb:10:in `block_code'
# ./lib/govuk_tech_docs/tech_docs_html_renderer.rb:90:in `block_code'
# ./spec/govuk_tech_docs/tech_docs_html_renderer_spec.rb:49:in `render'
# ./spec/govuk_tech_docs/tech_docs_html_renderer_spec.rb:49:in `block (3 levels) in <top (required)>'
# ./spec/govuk_tech_docs/tech_docs_html_renderer_spec.rb:85:in `block (4 levels) in <top (required)>'
2) GovukTechDocs::TechDocsHTMLRenderer#render a code block with syntax highlighting renders the code with syntax highlighting
Failure/Error: fragment = Nokogiri::HTML::DocumentFragment.parse(super)
NoMethodError:
undefined method `each' for nil:NilClass
opts.each { |k, v| @options[k.to_s] = v }
^^^^^
# ./vendor/bundle/ruby/3.1.0/gems/rouge-3.30.0/lib/rouge/lexer.rb:325:in `initialize'
# ./vendor/bundle/ruby/3.1.0/gems/rouge-3.30.0/lib/rouge/lexer.rb:97:in `new'
# ./vendor/bundle/ruby/3.1.0/gems/rouge-3.30.0/lib/rouge/lexer.rb:97:in `find_fancy'
# ./vendor/bundle/ruby/3.1.0/gems/middleman-syntax-3.6.0/lib/middleman-syntax/highlighter.rb:13:in `highlight'
# ./vendor/bundle/ruby/3.1.0/gems/middleman-syntax-3.6.0/lib/middleman-syntax/redcarpet_code_renderer.rb:10:in `block_code'
# ./lib/govuk_tech_docs/tech_docs_html_renderer.rb:90:in `block_code'
# ./spec/govuk_tech_docs/tech_docs_html_renderer_spec.rb:49:in `render'
# ./spec/govuk_tech_docs/tech_docs_html_renderer_spec.rb:49:in `block (3 levels) in <top (required)>'
# ./spec/govuk_tech_docs/tech_docs_html_renderer_spec.rb:89:in `block (4 levels) in <top (required)>'
```
This seemed odd because we hadn't done anything to change this area in
the intervening time.
After some investigations of if it could be related to a ruby change
(and a slight adventure where a very old Gemfile.lock meant we couldn't
reproduce this locally :facepalm:), it was pointed out that
middleman-syntax had an update on Jule 23rd 2025 to v3.6.0:
https://github.com/middleman/middleman-syntax/releases/tag/v3.6.0
This seems to be specifically about lexer, which draws attention:
middleman/middleman-syntax#92
So what do I think is going on here?
Well, Lexer seems to be throwing the error, apparently because the `opts`
parameter which should be a hash, is being passed as nil.
We can see why that would cause problems when calling the enumerator
here:
https://github.com/rouge-ruby/rouge/blob/3b461b1ffe5fc6416373df8c3c35da83a283606d/lib/rouge/lexer.rb#L323
This gets called from lexer's find_fancy class, both nothing has changed
there nor is there anything that I can see that mutates opts to nil in
either `lookup_fancy` or `find_fancy`:
https://github.com/rouge-ruby/rouge/blob/3b461b1ffe5fc6416373df8c3c35da83a283606d/lib/rouge/lexer.rb#L46
https://github.com/rouge-ruby/rouge/blob/3b461b1ffe5fc6416373df8c3c35da83a283606d/lib/rouge/lexer.rb#L94
So we're back in our trace into middleman-syntax, here's where
find_fancy gets called:
https://github.com/middleman/middleman-syntax/blob/d5042d6a583494aad3ceb0517685e945aa093d9b/lib/middleman-syntax/highlighter.rb#L13
Line 13 must make it through lexer without error before
Rogue::Lexer::PLainText can be offered as a fallback. And at this point
in a debugger i'm seeing that `lexer_options` has become nil!
So I think this line is sus:
https://github.com/middleman/middleman-syntax/blob/d5042d6a583494aad3ceb0517685e945aa093d9b/lib/middleman-syntax/highlighter.rb#L11
on line 11, the helper is attempting to delete lexer_options but when
there is no key of `lexer_options` then it's returning nil, which is
expected behaviour from delete.
https://ruby-doc.org/3.4.1/Hash.html#method-i-delete
You can test this with:
```
lexer_options = highlighter_options.delete(:lexer_options)
lexer_options
```
When I think what they want is:
```
lexer_options = {}.delete_if {|k,v| k == :lexer_options}
```
I'll open an issue over there, but for the time being for syntax
highlighting to continue to work we want to stay locked to v3.5.0.
This commit can be disregarded and the lock removed once we think this
problem has been resolved and the existing test suite passes.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Changes Made:
Fixed the core issue in
/lib/middleman-syntax/highlighter.rb:lexer_optionsfromhighlighter_optionsbefore callingfind_fancy()lexer_optionsas the third parameter toRouge::Lexer.find_fancy()lexer_optionsparameter fromlexer.lex()call (which was causing deprecation warnings)Added comprehensive test in
/features/lexer_options.feature:myshell>>>is parsed as single prompt instead ofmyshell>+>>operatorUpdated step definitions in
/features/support/step_definitions.rb:Updated
.gitignore:vendor/bundleto exclude bundled dependenciesTechnical Details:
The issue was that
lexer_optionswere being extracted from the configuration but only passed tolexer.lex(), which doesn't accept options (causing deprecation warnings). The Rouge API requires lexer options to be passed toRouge::Lexer.find_fancy()as the third parameter to properly configure the lexer instance.Test Results:
Fixes #72.
💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.