Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch from ANSITerminal to ocolor #474

Merged
merged 6 commits into from
Jun 9, 2023
Merged

Switch from ANSITerminal to ocolor #474

merged 6 commits into from
Jun 9, 2023

Conversation

AltGr
Copy link
Contributor

@AltGr AltGr commented Jun 8, 2023

ocolor uses OCaml's Format module support for semantic tags; hence we can keep
using the usual formatting functions, but use format-string tags such as
@{<blue>some blue text@} inside them. There are several benefits, including
readability, correct formatting (the format characters are automatically skipped
from column calculations), and correct nesting of tags.

To properly leverage this, this PR also cleans up the whole printing pipeline,
ensuring we use Formatters all the way through (we used to have intermediate
string conversions in many places, which breaks the formatting paradigm).

In addition, this allows us to delay the decision to print color codes or not to
the backend printer, e.g. printing functions are not concerned wether color is
enabled, and it is done by detecting if the end output is a tty or a file (this
can still be forced by the --color option of course)

Another small change is that errors, warnings and logs are now all printed to
stderr, reserving stdout to normal program output (e.g. a .dcalc file that you
may want to redirect)

@AltGr
Copy link
Contributor Author

AltGr commented Jun 8, 2023

Closes #451

Copy link
Contributor

@denismerigoux denismerigoux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This is so cool!

compiler/catala_utils/cli.mli Show resolved Hide resolved
compiler/catala_utils/messages.ml Show resolved Hide resolved
if !Cli.disable_warnings_flag then Format.ifprintf Format.std_formatter format
else Format.printf ("%a" ^^ format ^^ "\n%!") warning_marker ()
let get_ppf = function
| Result -> Lazy.force std_ppf
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Lazy ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tere are a few reasons:

  • we want to do the setup only once (isatty is not free ; and the Format setup uses side-effects)
  • we need the command-line to already have been parsed before we can do that setup¹
  • also, we recreate a formatter at each call (for consistency among formatters for file output, etc.) and certainly don't want to use multiple concurrent formatters to stdout (that could be fixed by using Ocolor_format.raw_std_formatter instead though)

¹ I agree that this is a bit fragile if one were to use Messages printing too early in the process; we could switch to hidden references and explicitely call setup functions upon parsing the command-line if that were a problem (but that would also be an issue with cli flags not consistently acknowledged).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be this comment in messages.mli that describes how we expect these side-effects to work I think :)


let of_message (s : string) : t = { message = s; positions = [] }
let of_string (s : string) : t =
{ message = (fun ppf -> Format.pp_print_string ppf s); positions = [] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the codebase of the compiler uses fmt instead of ppf as the go-to variable names for formatters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I know (or at least it used to 😇)
The reason for my preference is that you often have functions that take a pretty-printing formatter and a format string as arguments, and fmt is ambiguous in that regard

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed

(match pos.message with None -> "" | Some msg -> msg ^ "\n")
(Pos.retrieve_loc_text pos.position))
pos))
format target "@[<v>%t%a@]" message
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouahou what's %t ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if it's a bit obscure, it's a function that just takes a ppf as argument. In the above "%t" message is basically equivalent to "%a" (fun ppf () -> message ppf) ()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, didn't know!

(Format.asprintf "\"%a\"" ScopeName.format_t scope_name)
(Cli.format_with_style [ANSITerminal.yellow])
(Format.asprintf "\"%a\"" Ast.ScopeDef.format_t scope_def_key))
"In scope @{<yellow>\"%a\"@}, the variable @{<yellow>\"%a\"@} is \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a big win!!!!

@AltGr
Copy link
Contributor Author

AltGr commented Jun 8, 2023

Thanks @denismerigoux for the review; I added to the PR the modified output of tests so you may want to double-check if there is no issue with that (tests were passing but that's because they tolerate whitespace diffs)

@@ -32,7 +32,8 @@ scope B:
```catala-test-inline
$ catala Interpret -s A
[RESULT] Computation successful! Results:
[RESULT] x =
[RESULT]
x =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure that we want this newline here between [RESULT] and x

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thing is, since we now ensure that pretty-printing is done consistently, the code below will be aligned with the x = ; so there is no clean/easy way to have neither the newline, nor the extra indentation as in the ERROR examples below.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum hum, the downside of doing things correctly :) Then we can leave it as it is in this PR right now.

@@ -29,8 +29,8 @@ scope Test1:
```catala-test-inline
$ catala Interpret -s Test1
[ERROR] Syntax error at token "scope"
Message: expected either 'condition', or 'content' followed by the expected variable type
Autosuggestion: did you mean "content", or maybe "condition"?
Message: expected either 'condition', or 'content' followed by the expected variable type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want that extra indent ? Also why are we keeping under CI something that has a syntax error?

@@ -13,8 +13,8 @@ scope Foo:
```catala-test-inline
$ catala Interpret -s Foo
[ERROR] Error during typechecking, incompatible types:
--> integer
--> bool
--> integer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we want this big indent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case (comparing with the above RESULT), we can probably tweak the printer for the specific error message to not have an outer box that creates this alignment.

[RESULT] Printing the tree of exceptions for the definitions of variable "x" of scope "Foo".
[RESULT] Definitions with label "base":
[RESULT]
Printing the tree of exceptions for the definitions of variable "x" of scope "Foo".
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I guess the newline here is a bit ugly

@AltGr AltGr merged commit 9577d57 into CatalaLang:master Jun 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants