Skip to content

Conversation

garazdawi
Copy link
Contributor

This PR adds a new module called io_ansi that allows the user to emit Virtual Terminal Sequences (aka ansi sequences) to the terminal in order to add colors/styling to text, or create fullyfledged terminal applications.

io_ansi uses the local terminfo database in order to be as cross-platform compatibly as possible.

It also works across nodes so that if functions on a remote node calls io_ansi:fwrite/1 it will use the destination terminals terminfo database to determine which sequences to emit. In practice this means that you can call things in a remote shell session that uses io_ansi and it will properly detects that terminal sequences the target terminal can handle and will print using them correctly.

At the same time all at-hoc ANSI escape sequence usage in Erlang/OTP has been migrated to use io_ansi and the terminal application user's guide has been updated.

@garazdawi garazdawi added this to the OTP-29.0 milestone Jun 13, 2025
@garazdawi garazdawi requested review from dgud and frazze-jobb June 13, 2025 13:26
@garazdawi garazdawi self-assigned this Jun 13, 2025
@garazdawi garazdawi added team:VM Assigned to OTP team VM feature labels Jun 13, 2025
Copy link
Contributor

github-actions bot commented Jun 13, 2025

CT Test Results

    5 files    288 suites   2h 58m 4s ⏱️
5 168 tests 4 817 ✅ 351 💤 0 ❌
6 589 runs  6 152 ✅ 437 💤 0 ❌

Results for commit a6e4754.

♻️ This comment has been updated with latest results.

To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.

See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.

Artifacts

// Erlang/OTP Github Action Bot

@garazdawi garazdawi force-pushed the lukas/stdlib/io_ansi branch from b17ffb1 to a6e4754 Compare June 17, 2025 08:27
@garazdawi garazdawi added the testing currently being tested, tag is used by OTP internal CI label Jun 17, 2025
Copy link
Contributor

@dgud dgud left a comment

Choose a reason for hiding this comment

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

Great work

Comment on lines +574 to +577
-doc """
Change background color to index color. `Index` 0-15 are equivilant to
the named colors in `t:background_color/0` in the order that they are listed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess you have already thought about this but why not have:
foreground/1 and background/1 accept color atoms directly?
Then you could remove all Color() and Color_background() functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, good point! we can definitely do that!

Comment on lines +877 to +879
?FUNCTION(negative_off).

-doc """
Copy link
Contributor

Choose a reason for hiding this comment

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

Similiar to previous comment, do we want a style(Style::atom()) function.

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 I don't think so as the style does not add any additional information while background/foreground does.

Comment on lines +1297 to +1298
?SPEC(cursor_report_position).
?FUNCTION(cursor_report_position).
Copy link
Contributor

Choose a reason for hiding this comment

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

Is a cursor_get_position() -> {Col:integer(), Row:integer()} feasible?
So I don't have to parse the ANSI codes, when debugging?
Or do I do that with one of the terminfo functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It might be possible. The problem I have found is that this function is rather slow, so I'm not sure it has any practical usecases... also there are no terminfo function for parsing stuff... so I'd have to do that myself.

Comment on lines +1301 to +1302
Activate the alternate screen.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe a short description of what alternate screen do?
Most of the other stuff is self explanatory but this I would have to go to other docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do!

Comment on lines +1650 to +1656
[case Data of
<<Value:(byte_size(Value))/binary, Rest/binary>> ->
throw({Key, Value, Rest});
_ ->
ok
end || Key := Values <- get_vts_mappings(),
Value <- Values],
Copy link
Contributor

Choose a reason for hiding this comment

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

This code is un-parseable for me :-)
Re-write with a fun() in the map-comprehension or something?

@garazdawi garazdawi removed the testing currently being tested, tag is used by OTP internal CI label Jun 18, 2025
@garazdawi garazdawi assigned frazze-jobb and unassigned garazdawi Jun 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature team:VM Assigned to OTP team VM
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants