Skip to content
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

add Lexer#with, for specifying options after initialization #1188

Closed
wants to merge 2 commits into from

Conversation

jneen
Copy link
Member

@jneen jneen commented Jun 13, 2019

cc @mojavelinux

A common use case is to want to set options on the return value of something like Lexer.find_fancy, which returns an instance rather than a class. While in the future we may want to allow those options to be passed to the #lex method, we must wait for a major version release to clear the deprecation of the :continue option.

In the meanwhile, since lexer initializers are uniform, we can provide a method :with that returns a cloned lexer with extra options, which should satisfy most use cases.

@mojavelinux
Copy link
Contributor

mojavelinux commented Jun 14, 2019

I like the idea of being able to reinitialize a Lexer with a new set of options. However, there might be something even simpler for the use case at hand, which I'll describe.

The difficulty with Lexer.find_fancy is that you don't know what the Lexer will be when preparing the options to pass in. This could be solved by allowing Lexer.find_fancy to accept a block which provides access to the resolved lexer class and compiled options. The block would return the lexer instance, replacing the default behavior.

Basically, we're looking to control this line:

lexer_class && lexer_class.new(additional_options)

Here's how that might look in practice:

start_inline_if_php = ... # user-defined

lexer = Lexer.find_fancy lang do |lexer_class, opts|
  if lexer_class.tag == 'php' && start_inline_if_php
    opts[:start_inline] = true
  end
  lexer_class.new opts
end

We get all the benefits of Lexer.find_fancy while still having control over how the lexer is instantiated. And that would solve the chicken-egg problem.

@mojavelinux
Copy link
Contributor

I should add that this would dovetail really nicely with guess too, when you absolutely have no idea what lexer you're going to get. You would now have a callback post-guess to react to the result.

@jneen
Copy link
Member Author

jneen commented Jun 14, 2019

In this latest commit, I've factored out a method called Lexer.lookup_fancy, which does not instantiate a lexer, but instead returns a pair of [lexer_class, opts] - I think this is a simpler way to meet the requirement. Your example would be:

lexer_class, opts = Lexer.lookup_fancy(lang)
lexer_class ||= Rouge::Lexers::PlainText

if lexer_class.tag == 'php' && start_inline_if_php
  # use string keys for lexer options (lexer option keys are user-provided, thus unsafe for symbols)
  # and use reverse_merge in case of a manual start_inline=false in the CGI options
  opts.reverse_merge!('start_inline' => true)
end

lexer_class.new(opts)

@jneen
Copy link
Member Author

jneen commented Jun 14, 2019

If you don't have reverse_merge then opts['start_inline'] = true unless opts.key?('start_inline') works just as well.

@jneen
Copy link
Member Author

jneen commented Jun 14, 2019

I will, however, note that a Lexer instance and a pair of lexer class and options are not that different when you get right down to it...

@pyrmont pyrmont added the needs-review The PR needs to be reviewed label Jun 14, 2019
#
def find_fancy(str, code=nil, additional_options={})

# Rlease note: the lexer class might be nil!
Copy link
Contributor

Choose a reason for hiding this comment

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

typo here:

Suggested change
# Rlease note: the lexer class might be nil!
# Release note: the lexer class might be nil!

@mojavelinux
Copy link
Contributor

What's the argument against the block argument? That seems to me like the more idiomatic way to do it in Ruby. And it's already used throughout the Rouge API. Returning multiple arguments from a function is always a bit awkward, especially in comparison.

I'd make the case that the block shouldn't be called if no lexer is resolved.

@pyrmont
Copy link
Contributor

pyrmont commented Jun 18, 2019

I didn't intend to split the discussion across two PRs. The fact the suggestions to @jneen's solution are visible in the PR list for Rouge is an unfortunate side effect of this branch being in the Rouge project.

Here's the explanation for my suggested changes:

Currently, revisions to Lexer are being discussed in #1188. This PR is intended to improve the solution provided in that branch.

The code proposed in #1188 includes a method Lexer.lookup_fancy that returns a Lexer subclass. This is in addition to the existing method Lexer.find_fancy that returns an instance of a Lexer subclass. This difference is not evident from the names of the methods and so this commit renames lookup_fancy to find_fancy_class.

Separately, the commit also adds support to consumers passing a block to find_fancy. It is not envisaged that block passing will be common but supporting it allows for consumers to use idiomatic Ruby to customise the instantiation of a lexer.

Finally, tests are added.

Please discuss here rather than in #1195.

@pyrmont pyrmont added author-action The PR has been reviewed but action by the author is needed and removed needs-review The PR needs to be reviewed labels Jun 18, 2019
@pyrmont pyrmont added maintainer-action The PR has been reviewed but action by a maintainer is required and removed author-action The PR has been reviewed but action by the author is needed labels Jul 2, 2019
@ashmaroli
Copy link
Contributor

@pyrmont Can you do a git pull upstream master onto this branch..? Thanks.

@pyrmont pyrmont force-pushed the feature.lexer-with branch from 3c54f92 to 451b978 Compare July 12, 2019 18:25
@pyrmont
Copy link
Contributor

pyrmont commented Feb 1, 2020

@jneen Should this branch be reworked to be part of the v4.0 release?

@jneen
Copy link
Member Author

jneen commented Jul 21, 2020

Superseded by #1565.

@jneen jneen closed this Jul 21, 2020
@pyrmont pyrmont removed the maintainer-action The PR has been reviewed but action by a maintainer is required label Jul 21, 2020
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.

4 participants