jaspervdj / patat

Terminal-based presentations using Pandoc
GNU General Public License v2.0
2.42k stars 60 forks source link

Add RGB support for themes #48

Closed Tenchi2xh closed 6 years ago

Tenchi2xh commented 6 years ago

Hi! Love your project, great upgrade from mdp 😁

I added RGB support colors for themes, as supported by System.Console.ANSI.

The format used in the configuration YAML is the following:

---
patat:
    theme:
        code:   [rgb#000, onRgb#aaa]
        emph:   [rgb#6af]
        header: [rgb#f80]
...

# An orange header!

I *love* `RGB`!

This renders as follows:

image

The colors are triplets of bytes, mapping #RGB to 0xRRGGBB, making for a total of 4096 different colors. Having this short format is enough for the purposes of text, is shorter to write, and also avoids a memory hog / freeze when opening patat: since sgrsByName contains all different SGR modes including the RGB ones, using the full space of #RrGgBb would generate 16 million entries (I tried, and it just freezes while loading for a very long time).

4096 colors is still quite a lot, so in order to avoid filling the screen with all of them for the error message, I filtered them out and added an explanation:

$ patat wrong.md
Error in $.theme.borders: Unknown style: "x". Known styles are: "bold", "dullBlack", ..., "vividYellow",
or "rgb#RGB" and "onRgb#RGB" where 'R', 'G' and 'B' are hexadecimal numbers (e.g. "rgb#f80").

I'm very far from being fluent in Haskell, so please point out anything that could be written better! This is actually my first Haskell open-source contribution. It's a simple feature but I spent many hours in ghci and reading the docs / source code of System.Console.ANSI and Data.Colour πŸ˜„

Tenchi2xh commented 6 years ago

Here's what can be achieved with this new feature:

screen shot 2018-08-24 at 22 18 03

(Here's with the default configuration for comparison)

It's a variant based on the Tomorrow theme available in multiple IDEs

Click for theme configuration ```yaml patat: theme: header: [rgb#d66] emph: [rgb#d95] codeBlock: [onRgb#222] syntaxHighlighting: keyword: [rgb#c9c] controlFlow: [rgb#c9c] import: [rgb#c9c] decVal: [rgb#d95] baseN: [rgb#d95] float: [rgb#d95] constant: [rgb#d95] char: [rgb#8cc] specialChar: [rgb#fff] string: [rgb#bd6] verbatimString: [rgb#bd6] comment: [rgb#999] function: [rgb#8be] variable: [rgb#d66] operator: [rgb#8cc] builtIn: [rgb#8be] preprocessor: [rgb#fc7] attribute: [rgb#8cc] normal: [rgb#ddd] ```
jaspervdj commented 6 years ago

Thanks a lot! This is a very neat and useful contribution, and I wouldn't be able to tell from the code that this was a first-time Haskell contribution :-)

I will try to give some detailed feedback so we can work from there and get this merged into the next release.

The main thing I would like to avoid is building the big list of colours at all. I think this can be done in the following way, by making a mental distinction between "names" and "strings" (even though they're just strings in the end...)

  1. We rename nameForSGR to sgrToString and support RGB there

  2. We sgrsByName to namedSgrs and don't support RGB here, it's just a table of named ones

  3. We add a new function, stringToSgr :: String -> Maybe Ansi.SGR which does the following:

    • If the string starts with rgb or onRgb, we do the RGB conversion/parsing
    • Else, if the string is the namedSgrs table, we look it up there
    • Else, we return Nothing
  4. The FromJSON style instance can now call stringToSgr.

This also makes it possible to support rgb#123456 colours later on.

Does this makes sense? Do you agree with this approach? Please let me know if you want to discuss this further. Thanks again for the PR!

Tenchi2xh commented 6 years ago

Hey that worked! Nice suggestions, I implemented it almost as you described (And thanks for the kind words!)

I'm always surprised when it compiles and then works haha.

I will post an updated 24-bit Tomorrow theme later on πŸ˜„ I'm thinking that maybe it would be nice to provide a folder like this in the root of the repo:

.
└── themes/
Β Β  β”œβ”€β”€ monokai.patat.yaml
Β Β  β”œβ”€β”€ solarized-light.patat.yaml
Β Β  β”œβ”€β”€ solarized-dark.patat.yaml
Β Β  └── tomorrow.patat.yaml
jaspervdj commented 6 years ago

Thanks, the code looks great now!