Skip to content

Render colours in help#539

Merged
bkirwi merged 13 commits intobkirwi:mainfrom
keynmol:colors
Jan 30, 2026
Merged

Render colours in help#539
bkirwi merged 13 commits intobkirwi:mainfrom
keynmol:colors

Conversation

@keynmol
Copy link
Copy Markdown
Contributor

@keynmol keynmol commented May 11, 2024

Primary goals:

  1. Colored output (exact design TBD)

CleanShot 2024-05-11 at 12 41 18

  1. Maintain compatibility

Non-goals:

  1. Extensibility (can happen later once we settle on theming design)

Copy link
Copy Markdown
Owner

@bkirwi bkirwi left a comment

Choose a reason for hiding this comment

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

Left a few comments, but overall on board!

Will still need to take some care with compatibility breaking, but I don't see any reason why we shouldn't be able to ship this in a compatible way...

Comment thread core/shared/src/main/scala/com/monovore/decline/HelpFormat.scala Outdated
Comment thread core/shared/src/main/scala/com/monovore/decline/HelpFormat.scala Outdated
Comment thread core/shared/src/test/scala/com/monovore/decline/HelpSpec.scala Outdated
import com.monovore.decline.HelpFormat.Plain
import com.monovore.decline.HelpFormat.Colors

private[decline] trait Theme {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I don't follow why we need both Theme and HelpFormat - seems like users could pass in a Theme directly. (And if we can get away with fewer concepts, strong preference for that!)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

seems like users could pass in a Theme directly

I treat Theme interface as almost an implementation details - the methods in it are very tightly coupled to the help rendering logic (they're by no means the minimal set of methods required to render something), and I don't think it's beneficial (at this point) to expose this interface to users at all - because it'd increase compatibility surface.

Alternatively I could make it all 1 concept and make every method in Theme private to address the concerns from previous paragraph - but I felt like keeping Theme separate and private is the easiest w.r.t. API surface and flexibility.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Yeah, it's true that the fact that it's not user-facing does mitigate it to a large extent. Willing to keep it as is for now...

}

def optionList(opts: Opts[_]): Option[List[(Opt[_], Boolean)]] = opts match {
private[decline] case class HelpArgs(
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Neat! This seems like something with this shape could eventually be cleaned up and made public for some of the fancier help-rendering ideas folks have had (HTML generation, etc.) but leaving it private for now seems like the right call.

@keynmol keynmol changed the title WIP: render colours in help Render colours in help Oct 30, 2025
@keynmol
Copy link
Copy Markdown
Contributor Author

keynmol commented Oct 30, 2025

@bkirwi happy 2025 :D

I've decided to give this PR another go and make it pass through MiMa. Needless to say it's a bit of a nightmare (96c00ed#diff-87e4ae97ec5803492891d3ecd23ca3f17075130de2101c2b72bb00bf6a837accR128) but I think I got it done.

@keynmol keynmol marked this pull request as ready for review October 30, 2025 10:56
@keynmol
Copy link
Copy Markdown
Contributor Author

keynmol commented Oct 30, 2025

While the changes are binary compatible, they're not source compatible (which I believe is allowed for a minor release in our semver conventions) – I was in fact hoisted by my own petard in my own project, where .copy(body = Nil) is no longer possible: https://github.com/indoorvivants/sn-bindgen/blob/main/modules/bindgen/src/main/scala/main.scala#L17

Copy link
Copy Markdown
Owner

@bkirwi bkirwi left a comment

Choose a reason for hiding this comment

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

Yeah, wow, that does look like a hassle! Looks like it worked out, though - Mima does seem happy on my end also.

I am, nevertheless, going to request some minor changes. Sorry! Hopefully a fairly light lift, but let me know if you feel otherwise.

Comment thread core/shared/src/test/scala/com/monovore/decline/HelpSpec.scala Outdated
/**
* Controls which sections of the Help are rendered in its string representation
*/
class RenderOptions private (opts: Flags) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Conceptually, what's the difference between a format and a render-options? Is there a reason that the two aren't combined in a single type?

(One of the joys of Decline is that its API surface is pretty small, so it'll be a kindness to combine these if we can.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have merged RenderOptions into HelpFormat: a00d099

Is this similar to what you had in mind?

@keynmol
Copy link
Copy Markdown
Contributor Author

keynmol commented Nov 10, 2025

Sorry! Hopefully a fairly light lift, but let me know if you feel otherwise.

No worries at all – and if you have any more comments withheld, let it rip, I don't want to land something that becomes a maintenance burden in the future.

@bkirwi
Copy link
Copy Markdown
Owner

bkirwi commented Nov 22, 2025

No worries at all – and if you have any more comments withheld, let it rip, I don't want to land something that becomes a maintenance burden in the future.

So! My original thought was that the "right" / most decline-ish way to deal with this sort of feature -- as well as similar requests to have a HTML or manfile renderer or generate completions -- was to capture the structure of the command line in Usage / Help and let folks pattern-match or fold over that. (~Similar to the Opts AST itself, but without the type param and with alternatives / applicatives flattened out for easy rendering.)

I still think this is a good idea, but:

  • Even if we did do that, it would be tempting to parameterize this specific --help rendering code by color / theme, since otherwise you'd end up either duplicating a bunch of code or having a bunch of very specific helpers.
  • I think going in this direction doesn't box us out of doing that in the future. (This API would just build on some more general API.)
  • I am not going to work on this sort of thing anytime soon, and this is valuable functionality that others will love.

So, very happy to merge this! But now you know the directional thinking too in case you end up wanting to follow up or whatever in the future.

@keynmol
Copy link
Copy Markdown
Contributor Author

keynmol commented Nov 22, 2025

I think going in this direction doesn't box us out of doing that in the future. (This API would just build on some more general API.)

Yeah, this would require de-casing the HelpArgs case class and all of the case classes it references, with a re-work of their public API to make it usable.

This is something I'm also interested in (as I'd like to make the rendering of my CLI options prettier for the website), but I'm unlikely to tackle that at the moment either.

@bkirwi bkirwi merged commit bf66ad6 into bkirwi:main Jan 30, 2026
4 checks passed
@bkirwi
Copy link
Copy Markdown
Owner

bkirwi commented Feb 8, 2026

Just wanted to confirm that this is out as 2.6.0... thanks again for the contribution, and sorry it took a minute to get it released!

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.

2 participants