Repository navigation
stdlib: add a format_text function for text-like format strings - #13569
Conversation
|
I think it would be nice to clarify and/or add a few things here:
|
|
There could be a shorter implementation, but I don't know if it is conceptually better -- you will be disappointed that there is less exciting GADT programming. Format has a machinery to parse a format string at runtime, and check that it is compatible with a given format type -- see Now that I think of it, it may actually be tricky to recognize, just in the format string, which spaces are part of the output and should turn into |
| type neutral_concat = | ||
| { f: | ||
| 'a 'b 'c 'd 'e 'f. [`String of string | `Char of char ] -> | ||
| ('a,'b,'c,'d,'e,'f) fmt -> ('a,'b,'c,'d,'e,'f) fmt |
There was a problem hiding this comment.
You could also express this as the following, if you wanted -- reusing the type of End_of_format as the generic type of parameter-less formats.
'f 'b 'c 'e. [ `String of string | `Char of char ] -> ('f, 'b, 'c, 'e, 'e, 'f) fmt
@gasche , I have the impression that in term of complexity using |
|
Thanks for your answers @Octachron would likely good to add them in some form to the doc string.
Note: the problem is that using |
|
For this point, I think it would be better to partially solve the issue at the output device level, see #13570 . |
|
I'm satisfied with the tweaked implementation. @dbuenzli, are you satisfied with the proposed specification? |
dbuenzli
left a comment
There was a problem hiding this comment.
I think some clarifications are needed.
| with hint breaks or forced newlines: | ||
| - Only ascii spaces ([" " = "\u{20}"]) and linefeeds ["\n" = "\u{A}"] are | ||
| replaced. In particular, this means that non-breaking spaces (for instance | ||
| ["\u{A0}"]) are left unchanged. |
There was a problem hiding this comment.
I think it would be better to talk in Format speak:
- Spaces
" "(U+0020) are replaced by"@ ". - Newlines
"\n"(U+000A) are replaced by"@<0 1>". - The rest is kept unchanged and in particular, non-breaking spaces like U+00A0.
| replaced. In particular, this means that non-breaking spaces (for instance | ||
| ["\u{A0}"]) are left unchanged. | ||
| - [0<=k<=1] newlines followed by [n] spaces are converted to a [@;<0 (k+n)>] | ||
| break hint (when [k+n>=1]). |
There was a problem hiding this comment.
Isn't that rule redundant with the space and newline replacing rules ? If not I'm not sure I understand its implications.
There was a problem hiding this comment.
Coalescing multiple spaces in a single break hint ensure that the line is split at most once in the beginning of the group of spaces. This ensures that the spaces in
format_text "a b"is translated to a single line break rather than multiple line breaks between blank lines.
There was a problem hiding this comment.
Ah yes I think I was confused by the sentence. It read as if this happened only after newlines, but k can be 0. I think the overall formulation is more difficult to understand that it could be. Wouldn't something like this work:
- First blank lines (lines made only of spaces) are replaced by two forced newlines
"@\n@\n". That is they remain blank in the output. - Remaining sequences made of
kspaces (U+0020) and newlines (U+000A) are replaced by@;<0 k>. That is they are eitherkspaces in the output or a single break. - Breaks can be avoided using-no break space characters (U+00A0).
- Lines can be forced by using
"@\n"
There was a problem hiding this comment.
Blank lines by themselves should map to a single @\n, it is the preceding line that must be a \@n too:
- Blank lines (lines made only of spaces) are replaced by
\@n. - Sequences of spaces and a newline preceding a blank line are replaced by
\@n: Blank lines remain blank lines in the output. - Remaining sequences made of
kspaces and newlines are replaced by@;<0 k>.
There was a problem hiding this comment.
I have added the new description (with a tweak that the space width is at least 1 on break hints) and adapted the implementation to match the behavior and the specification.
There was a problem hiding this comment.
(Does @dbuenzli's thumb up mean that he approves the specification you converged towards? If so, please feel free to merge.)
| *) | ||
|
|
||
| val format_text: ('a,'b,'c,'d,'e,'f) format6 -> ('a,'b,'c,'d,'e,'f) format6 | ||
| (** [text fmt] replaces spaces and newlines in the format string literal [fmt] |
There was a problem hiding this comment.
| (** [text fmt] replaces spaces and newlines in the format string literal [fmt] | |
| (** [format_text fmt] replaces spaces and newlines in the format string literal [fmt] |
| match newlines, spaces with | ||
| | (0|1), spaces -> | ||
| let break = Break("", max spaces 1, 0) in | ||
| String_literal(before, cons ~repeat:1 break len s pos fmt) | ||
| | bl, _ -> | ||
| String_literal( | ||
| before, | ||
| cons ~repeat:bl Force_newline len s pos fmt | ||
| ) |
There was a problem hiding this comment.
| match newlines, spaces with | |
| | (0|1), spaces -> | |
| let break = Break("", max spaces 1, 0) in | |
| String_literal(before, cons ~repeat:1 break len s pos fmt) | |
| | bl, _ -> | |
| String_literal( | |
| before, | |
| cons ~repeat:bl Force_newline len s pos fmt | |
| ) | |
| let repeat, break = | |
| match newlines, spaces with | |
| | (0|1), spaces -> 1, Break("", max spaces 1, 0) | |
| | bl, _ -> bl, Force_newline | |
| in | |
| String_literal(before, cons ~repeat break len s pos fmt) |
this functions replaces spaces and newlines ([' '] and ['\n']) by Format formatting hints in format string literals. This make it easier to mix free-flowing text with [Printf] specifiers, boxes and tags.
and simplify the implementation along the way
d4b6cac to
21e840f
Compare
When converting warning messages to
Format_doc.t, it was quite annoying to add formatting hints on every space in text paragraphs, for instance:In order to remove this annoyance, this PR propose to add a
format_textfunction which replaces spaces and newlines ([' '] and ['\n']) with formatting hints in format string literals. This means that with this new function, one can writeand obtain a better version of the formatted string literal above.
Compared to
pp_print_string,format_textis less efficient (it may allocate a copy of the string) but it does not require to separate the text contents from the specifiers, formatting tags and other formatting hints making it easierto use in many context.