The writefln
function has a lot of formatting options for strings, but they always come out with the same default color. How can we create colored text output in D?
Let's write colored output to the console by executing the following steps:
terminal
.Terminal
with a linear output type.terminal.color
method.terminal.write
, terminal.writeln
, terminal.writef
, or terminal.writefln
functions. They work the same way as the homonym functions in std.stdio
.terminal.flush()
if it isn't done soon enough automatically.terminal.d
together with dmd yourfile.d terminal.d
.The following is the code:
import terminal; void main() { auto terminal = Terminal(ConsoleOutputType.linear); terminal.color(Color.green, Color.red); terminal.writeln("Hello, world!"); }
Running the program will print Hello, world! in green text on a red background.
Writing colored output is carried out differently on a Unix terminal than on the Windows console. On a Unix terminal, colors are set by printing an ANSI escape sequence to stdout
, and it is the program's responsibility to set it back to normal before termination. On the Windows console, colors are changed with an API function, and the operating system will ensure the next program run sees a consistent state, so you do not have to clean it up manually.
The terminal.d
module hides these implementation details behind a consistent interface, with the Terminal
struct performing necessary cleanup work in its destructor.
In an earlier draft of terminal.d
, I used an array of delegates created in the constructor to roll back changes in the destructor. That was a mistake that led to random crashes because delegates have a hidden internal pointer to the object. Using internal pointers in D's structs invokes undefined behavior because the struct may move at any time without notice, leaving a dangling pointer to the old, now reused memory. While a list of cleanup delegates may look elegant like scope(exit)
, alas it is bug prone and should not be used with structs.
Terminal
operates on a unique resource that is modified in its destructor, and it has a disabled postblit to statically prohibit copying the object. Similarly, the default construction is disabled to force the user to call the explicit constructor, giving Terminal
a chance to run necessary initialization functions and gather information about the terminal's existing state at startup.
Otherwise, the implementation of Terminal
is pretty unremarkable—it is just a wrapper for operating system and library functionality with its own buffering solution.
ncurses
, which provides similar functionality to terminal.d
. For advanced uses, ncurses
may provide better compatibility with your system.3.133.142.2