pqwy / notty

Declarative terminal graphics for OCaml

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

possible issue with Notty.I.strf

cedlemo opened this issue · comments

I wanted to add tab space in a string image like with Format.astring with :

I.(strf ~attr:A.(fg lightred ++ bg lightblack) "\t %s : %s" title artist)

but it makes my program stop without any warnings, when I remove the "\t" part, everything is fine.

If I add a backslash to try to escape,

I.(strf ~attr:A.(fg lightred ++ bg lightblack) "\\t %s : %s" title artist)

my program does not crash but it displays something like:

"\ta_title : an artist" which is not the same behavior that Format.astring as it is said in the documentation

pretty-prints like Format.asprintf format ..., but returns an image.

Regards.

commented

Interesting... what happens if you run that in the toplevel?

Notty.I.(strf ~attr:Notty.A.(fg lightred ++ bg lightblack) "\t %s : %s" "titl" "artist");;
Exception: Invalid_argument "Notty: control char: U+09, \"\\t \"".
commented

So there is a warning after all? Exciting!

What could it mean?

So there is a warning after all? Exciting!

are you sarcastic ? does this issue bother you or the way I writted it (did you find me rude, if so I sincerly apologize)?

It meens that this kind of character is not accepted by Notty.I.strf unlike Format.astring

commented

This is because images cannot contain control characters, ever, regardless of how you create them.

So every function that creates them from raw string or character material has to throw that exception. I updated the docs to mention the same thing for strf too, but that constraint was made pretty up-front and obvious.

If you are trying to simply pad on the left, you can use hpad and friends. If you are trying to right-align the title column, you need to form the columns separately.

(* maxby : ('a -> int) -> 'a list -> int *)
let maxby f xs = List.(fold_left max 0 (map f xs))
(* column : align:[`Left|`Middle|`Right] -> image list -> image *)
let column ~align images =
  let width = maxby I.width images in
  List.map (I.hsnap ~align width) images |> I.vcat

Assuming you have songs : string * string, you separate them into lists with information for different columns, create images individually, form the columns, and combine them into an overall image:

(* render_title : string -> image *)
let render_title = I.string A.(fg lightred ++ bg lightblack)
(* render_artist : string -> image *)
let render_artist = I.string A.(fg lightgreen ++ bg lightblack)

let (titles, artists) = List.split songs
let output =
  (List.map render_title titles |> column ~align:`Right)
  <|> (List.map render_artist artists |> column ~align:`Left)

Finally, you can add a column separator in various ways.

So every function that creates them from raw string or character material has to throw that exception. I updated the docs to mention the same thing for strf too, but that constraint was made pretty up-front and obvious.

Oh, ok,

now I do understand why you reacted that way, I was so focused on Format.astring that I totally forgot
the principles/goal of Notty.

again I sincerly apologize for bothering you with this, and thanks you for taking the time to help me.

commented

Heh, no need to apologize. Especially not sincerely. 😃

Here is a way to see the problem:

# print_string "\t]\n"; print_string "[\t]\n";;
        ]
[       ]
- : unit = ()

so if you remove the checks you get

# let i1, i2 = I.(string A.empty "[", string A.empty "\t]") ;;
val i1 : Notty.image = <abstr>
val i2 : Notty.image = <abstr>

# Notty_unix.(output_image (i2 <-> (i1 <|> i2) |> eol)) ;;
        ]
[       ]
- : unit = ()

so you can see that I.width i2 = I.width (i1 <|> i2). Therefore I.width i2 = I.width i1 + I.width i2, so I.width i2 = 1 + I.width i2. What is the width of i2, then? ℵ0?

It turns out the width is context-dependent, in the sense that the space i2 occupies varies dependent on the position it is printed at, as tabs align to a grid. As you can imagine, this completely destroys any calculations you can do with such images.

Every single control character will cause similar breakdown. Notty's nice API is just incompatible with allowing them in the output.

Some of them, like \n, I could parse ahead of time and turn into appropriate images (cf. #9). Others, like \t, just have no interpretation as an image at all. So I disallow them all to keep it simple and predictable.