aeson-pretty icon indicating copy to clipboard operation
aeson-pretty copied to clipboard

Add an option to color the output

Open chrismwendt opened this issue 11 years ago • 3 comments

Implementation

A function to convert a JSON Value to a list of color commands [SGR] from the ansi-terminal package has been added to both Config and PState.

defConfig still has the same behavior of no color (using noColors).

encodePretty still has the default behavior of no color (implemented as encodePretty' defConfig)

encodePretty' colorConfig colors the output by inserting color codes which are interpreted by the terminal.

Example

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson.Encode.Pretty
import Data.Aeson (Value(..), ToJSON(..), eitherDecode)
import qualified Data.Aeson.Encode as Aeson
import qualified Data.ByteString.Lazy as BS
import qualified Data.ByteString.Lazy.Char8 as BSC

someJSON :: BS.ByteString
someJSON = "[null,1337,true,\"hi\nthere\",{\"prop\":\"value\",\"p2\":false,\"p3\":3,\"p4\":\"hi\",\"p5\":null}]"

main :: IO ()
main = do
    BSC.putStrLn $ either BSC.pack encodePretty (eitherDecode someJSON :: Either String Value)
    BSC.putStrLn $ either BSC.pack (encodePretty' colorConfig) (eitherDecode someJSON :: Either String Value)

Output to terminal

image

Piped into Vim

image

chrismwendt avatar Sep 13 '14 06:09 chrismwendt

I like this feature, thanks for the pull request. But the code needs some more work.

  • I prefer confColors :: Value -> [SGR] to the Value -> String version, since it is more specifically typed. This also ensures, that no invalid codes are created.

  • There are empty objects passed to the confColors function, instead of the object actually colored. This means I can not change colors depending on the object's content. E.g. change background color of arrays with more than three elements. The type of confColors suggests this is possible.

  • Arrays use the colors for objects (the "punctuation" value)

  • Arguably, some Resets should always be inserted after the elements of an array or object. If you have a color configuration like this

    colors :: (Value -> String) colors (Array _) = setSGRCode [SetColor Foreground Vivid White] colors (String _) = setSGRCode [Reset, SetColor Background Vivid Red] ...

the background of the String will "leak out" of a surrounding array. I am unsure if a Reset should also be inserted at the very end, to not leak into any following data.

  • There is some duplication: toColor pstColor (object []) is all over the place, the "punctuation" (why that name?) should be moved into fromCompound. Perhaps pstColor should have type Value -> Builder otherwise you always call toColor pstColor.

informatikr avatar Oct 19 '14 15:10 informatikr

  • Agreed, Value -> [SGR] is better because it prevents invalid color codes.
  • I feel like color should be independent of content, so something like JSONType -> [SGR] would be even better. However, I don't think it's possible to pass the tag without the values, so I believe JSONType would need to be a new data type.

We could go a step further and define colors for every piece of output (like some kind of syntax tree data type), including different colors for each punctuation character (and whitespace too): {}[],:". That might be overkill, though.

  • Arrays probably should not share colors with Objects. The only reason I did that was so that all punctuation characters had the same color.
  • You're right, a Reset needs to be inserted before and after every element to prevent color bleed.
  • Yes, the multiple toColor pstColor (object []) are annoying. That and toColor could probably be cleaned up. And yes, the punctuation should be moved into fromCompound.

chrismwendt avatar Oct 20 '14 02:10 chrismwendt

I like Value -> [SRG] because it offers a superset of the JSONType -> [SRG] functionality, while using existing types. You are right, that color-by-content is an unlikely case, but most people will probably use the default colors anyway and won't bother with defining their own. The experts will have to deal with the additional options. And pattern matching the Value constructors with underscores for the content (as you did in defColors) is very declarative, in my opinion. The definition of defColors could even be copied into the Haddock, as an example.

Regarding the "step further", yes I think that goes too far. :-)

informatikr avatar Oct 20 '14 09:10 informatikr