Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elm: How to pretty print the model in the browser?

Tags:

elm

This question is kind of silly but i didn't find a straight forward solution.

Assuming I have a model that resembles this: - at least this big.

initModel =
{ selectedCategory = "Vacantion"
, context = "root/Work"
, abstractSyntaxTree =
    [ { categoryName = "Work"
      , categoryContent =
            []
      }
    , { categoryName = "Vacation"
      , categoryContent =
            [ { folderName = "Hawaii"
              , folderContent =
                    FolderContent
                        ( [ { folderName = "Booking"
                            , folderContent = FolderContent ( [], [] )
                            }
                          ]
                        , [ "flightTicket.jpg" ]
                        )
              }
            ]
      }
    ]
}

Question: How can i display it in the browser so that it looks good? - nothing fancy - just to see what's happening like a quick debugger..

What I've tried so far:

view = 
    div [] 
        [ pre [style [("width", "300") ] ] [ text (toString model)]
        ]

Works great on smaller models but on this case, I get this long -single line - formatted json like structure: enter image description here

I think this is the Problem: The prettify extension i installed in google chrome doesn't know how to deal with strings that do not contain \n in them. To check for this, i manually added a \n - and that string was split on the second row, as expected.

The output form text (toSting model) - it's a string that has no \n in it - so that's why everything is displayed on a single line in the browser - regardless of the 300 px limit on width.

Splitting up the string - by adding \n by myself - works except i don't know where exactly to add the \n. To make it dynamic, this requires a full-flagged parser for the model. A way to know where the expression starts, where is the next matching bracket .. etc. I'm not good enough to build this parser. I feel i'm over complicating this stuff. Must be a better solution..

How do you folks do it?

like image 957
AIon Avatar asked Nov 09 '16 23:11

AIon


3 Answers

Elm does not allow you to enumerate over the items in a record. For type-safety no doubt. So there is no "clean" way to do display a record nicely.


UPDATE: The solution below will not work anymore as of Elm 0.19.

It relies on a (deprecated) function toString that would convert any type into a string. Since Elm 0.17 (when I made this), Elm had already released a great debugger in 0.18, which already has a feature to follow model and messages in a separate window.


For debugging, you can do some trickery with toString to print it more neatly. Below is example code, which you can copy/ paste to elm-lang.org/try.

Simply pass any record to the viewModel function, and it displays it in the browser. It is quite crude, and probably not very performant with large records, but it will do the job..

import Html exposing (Html, text, div, p, pre)
import Html.Attributes exposing (style)
import String

quote = "\""
indentChars = "[{("
outdentChars = "}])"
newLineChars = ","
uniqueHead = "##FORMAT##"
incr = 20


model = 
  { name = "Abe"
  , age = 49
  , someTuple = (18,49)
  , relatives = [ "Claire", "Bill" ]
  , comments = "any special characters like []{}, will not be parsed"
  , cars = [ { brand = "BMW", model = "535i" } ]
  }

viewModel : a -> Html msg
viewModel model =
  let
    lines =
      model
      |> toString
      |> formatString False 0 
      |> String.split uniqueHead
  in
    pre [] <| List.map viewLine lines

viewLine : String -> Html msg
viewLine lineStr =
  let
    (indent, lineTxt) = splitLine lineStr
  in
    p [ style 
        [ ("paddingLeft", px (indent))
        , ("marginTop", "0px")
        , ("marginBottom", "0px")
        ] 
      ]
      [ text lineTxt ]


px : Int -> String
px int =
  toString int
  ++ "px"

formatString : Bool -> Int -> String -> String
formatString isInQuotes indent str =
  case String.left 1 str of
    "" -> ""

    firstChar -> 
      if isInQuotes then
        if firstChar == quote then
          firstChar 
          ++ formatString (not isInQuotes) indent (String.dropLeft 1 str)
        else
          firstChar 
          ++ formatString isInQuotes indent (String.dropLeft 1 str)
      else
        if String.contains firstChar newLineChars then
          uniqueHead ++ pad indent ++ firstChar
          ++ formatString isInQuotes indent (String.dropLeft 1 str)
        else if String.contains firstChar indentChars then
          uniqueHead ++ pad (indent + incr) ++ firstChar
          ++ formatString isInQuotes (indent + incr) (String.dropLeft 1 str)
        else if String.contains firstChar outdentChars then
          firstChar ++ uniqueHead ++ pad (indent - incr)
          ++ formatString isInQuotes (indent - incr) (String.dropLeft 1 str)
        else if firstChar == quote then
          firstChar 
          ++ formatString (not isInQuotes) indent (String.dropLeft 1 str)
        else
          firstChar 
          ++ formatString isInQuotes indent (String.dropLeft 1 str)

pad : Int -> String
pad indent =
  String.padLeft 5 '0' <| toString indent

splitLine : String -> (Int, String)
splitLine line =
  let
    indent = 
      String.left 5 line
      |> String.toInt
      |> Result.withDefault 0
    newLine =
      String.dropLeft 5 line
  in
    (indent, newLine)

main =
  viewModel model
like image 51
wintvelt Avatar answered Sep 29 '22 18:09

wintvelt


By now we have a nice package for 0.19.

elm install ThinkAlexandria/elm-pretty-print-json

And run

json = """{"name": "Arnold", "age": 70, "isStrong": true,"knownWeakness": null,"nicknames": ["Terminator", "The Governator"],"extra": {"foo": "bar","zap": {"cat": 1,"dog": 2},"transport": [[ "ford", "chevy" ],[ "TGV", "bullet train", "steam" ]]}}"""

{-| Formating configuration.
`indent` is the number of spaces in an indent.
`columns` is the desired column width of the formatted string. The formatter
will try to fit it as best as possible to the column width, but can still
exceed this limit. The maximum column width of the formatted string is
unbounded.
-}
config = {indent = 4, columns = 80}

-- run prettifier
Result.withDefault "" (Json.Print.prettyString config json)

-- result
{-
{
    "extra": {
        "transport": [
            [
                "ford",
                "chevy"
            ],
            [
                "TGV",
                "bullet train",
                "steam"
            ]
        ],
        "zap": {
            "dog": 2,
            "cat": 1
        },
        "foo": "bar"
    },
    "nicknames": [
        "Terminator",
        "The Governator"
    ],
    "knownWeakness": null,
    "isStrong": true,
    "age": 70,
    "name": "Arnold"
}
-}
like image 20
Tails Avatar answered Sep 29 '22 17:09

Tails


I found this in slack : elm-debug-transformer

how to pretty print elm model or elm types in the browser

Not rely what i asked for, because for example im gonna need to make it work with node.js also, but still seems like good solution for now.

like image 22
AIon Avatar answered Sep 29 '22 16:09

AIon