## ansi-terminal-prettyprint-output-stream.pkg
#
# Prettyprinting to ANSI terminals.
# This device supports the standard ANSI output attributes.
#
# For an overview of prettyprinter output stream functionality see
#
#
src/lib/prettyprint/big/src/out/prettyprint-output-stream.api#
# Compare to:
#
#
src/lib/prettyprint/big/src/out/plain-prettyprint-output-stream.pkg#
src/lib/prettyprint/big/src/out/html-prettyprint-output-stream.pkg# Compiled by:
#
src/lib/prettyprint/big/prettyprinter.lib### "We should give society not what
### it asks for, but what it needs."
###
### -- E.J. Dijkstra
stipulate
package at = ansi_terminal; # ansi_terminal is from
src/lib/src/make-ansi-terminal-escape-sequence.pkg package fil = file__premicrothread; # file__premicrothread is from
src/lib/std/src/posix/file--premicrothread.pkg package ns = number_string; # number_string is from
src/lib/std/src/number-string.pkg package wnx = winix__premicrothread; # winix__premicrothread is from
src/lib/std/winix--premicrothread.pkgherein
api Ansi_Terminal_Prettyprint_Output_Stream {
#
include api Prettyprint_Output_Stream # Prettyprint_Output_Stream is from
src/lib/prettyprint/big/src/out/prettyprint-output-stream.api where
Texttraits == List( at::Texttrait );
# Create an output stream. If the underlying stream is connected to a TTY,
# then styled output is enabled, otherwise it will be disabled.
make_ansi_terminal_output_stream
:
{ output_stream: fil::Output_Stream
}
->
Prettyprint_Output_Stream;
# enable/disable/query traitful output.
#
# texttrait_mode (dev, NULL) -- query current mode
# texttrait_mode (dev, THE TRUE) -- enable traitful output
# texttrait_mode (dev, THE FALSE) -- disable traitful output
#
# This function returns the previous state of the output stream.
# NOTE: this function raises DIE if called while a trait is active.
texttrait_mode: (Prettyprint_Output_Stream, Null_Or(Bool)) -> Bool;
};
package ansi_terminal_prettyprint_output_stream
: (weak) Ansi_Terminal_Prettyprint_Output_Stream
{
State
=
{ fg: Null_Or( at::Color ), # NULL is default color for terminal
bg: Null_Or( at::Color ), # NULL is default color for terminal
bold: Bool,
blink: Bool,
ul: Bool,
reverse: Bool,
invis: Bool
};
init_state
=
{ fg => NULL,
bg => NULL,
bold => FALSE,
blink => FALSE,
ul => FALSE,
reverse => FALSE,
invis => FALSE
};
# Compute the commands to transition from one state to another
#
fun transition (s1: State, s2: State)
=
{ fun needs_color_reset proj
=
case (proj s1, proj s2)
#
(THE _, NULL) => TRUE;
_ => FALSE;
esac;
fun needs_reset proj
=
case (proj s1, proj s2)
#
(TRUE, FALSE) => TRUE;
_ => FALSE;
esac;
# Does the state transition require resetting the attributes first?
#
reset = needs_color_reset .fg
or needs_color_reset .bg
or needs_reset .bold
or needs_reset .blink
or needs_reset .ul
or needs_reset .reverse
or needs_reset .invis;
# Compute the commands to set the foreground color
#
mv = case (reset, s1.fg, s2.fg)
#
(FALSE, THE c1, THE c2)
=>
if (c1 == c2 ) []; else [at::FG c2];fi;
(_, _, THE c) => [at::FG c];
(_, _, NULL) => [];
esac;
# Compute the commands to set the background color
#
mv = case (reset, s1.bg, s2.bg)
#
(FALSE, THE c1, THE c2)
=>
if (c1 == c2 ) mv; else at::FG c2 ! mv;fi;
(_, _, THE c) => at::BG c ! mv;
(_, _, NULL) => mv;
esac;
# Compute the commands to set the other display attributes:
#
fun add (proj, cmd, mv)
=
if ((reset or not (proj s1)) and proj s2) cmd ! mv;
else mv;
fi;
mv = add (.bold, at::BF, mv);
mv = add (.blink, at::BLINK, mv);
mv = add (.ul, at::UL, mv);
mv = add (.reverse, at::REV, mv);
mv = add (.invis, at::INVIS, mv);
case (reset, mv)
#
(FALSE, []) => "";
(TRUE, []) => at::to_string [];
(TRUE, mv) => at::to_string [] + at::to_string mv;
(FALSE, mv) => at::to_string mv;
esac;
};
# Apply a command to a state:
#
fun update_state1 (cmd, { fg, bg, bold, blink, ul, reverse, invis } )
=
case cmd
#
at::FG c => { fg=>THE c, bg, bold, blink, ul, reverse, invis };
at::BG c => { fg, bg=>THE c, bold, blink, ul, reverse, invis };
at::BF => { fg, bg, bold=>TRUE, blink, ul, reverse, invis };
at::BLINK => { fg, bg, bold, blink=>TRUE, ul, reverse, invis };
at::UL => { fg, bg, bold, blink, ul=>TRUE, reverse, invis };
at::REV => { fg, bg, bold, blink, ul, reverse=>TRUE, invis };
at::INVIS => { fg, bg, bold, blink, ul, reverse, invis=>TRUE };
esac;
# Apply a sequence of commands to a state:
#
fun update_state ( [], traits) => traits;
update_state (cmd ! r, traits) => update_state (r, update_state1 (cmd, traits));
end;
Texttraits = List( at::Texttrait );
Prettyprint_Output_Stream
=
PRETTYPRINT_OUTPUT_STREAM
{
mode: Ref( Bool ),
output_stream: fil::Output_Stream,
stk: Ref( List( State ) )
};
fun top [] => init_state;
top (st ! r) => st;
end;
fun same_texttraits (s1: Texttraits, s2)
=
s1 == s2;
fun push_texttraits (PRETTYPRINT_OUTPUT_STREAM { mode, output_stream, stk }, traits)
=
if *mode
#
cur_st = top *stk;
new_st = update_state (traits, cur_st);
fil::write (output_stream, transition (cur_st, new_st));
stk := new_st ! *stk;
fi;
fun pop_texttraits (PRETTYPRINT_OUTPUT_STREAM { mode, output_stream, stk } )
=
if *mode
#
case *stk
#
[] => ();
cur_st ! r
=>
{ new_st = top r;
#
fil::write (output_stream, transition (cur_st, new_st));
stk := r;
};
esac;
fi;
fun default_texttraits _
=
[];
fun is_tty out_s # Return TRUE iff an output_stream is a TTY.
=
{ (fil::pur::get_writer (fil::get_outstream out_s))
->
(winix_base_text_file_io_driver_for_posix__premicrothread::FILEWRITER { io_descriptor, ... }, _);
case io_descriptor
#
THE iod => (wnx::io::iod_to_iodkind iod == wnx::io::CHAR_DEVICE);
_ => FALSE;
esac;
};
fun make_ansi_terminal_output_stream { output_stream }
=
PRETTYPRINT_OUTPUT_STREAM {
output_stream,
mode => REF (is_tty output_stream),
stk => REF []
};
fun put_string (PRETTYPRINT_OUTPUT_STREAM { output_stream, ... }, s) # Write a string in the current texttraits to the output stream.
=
fil::write (output_stream, s);
fun flush (PRETTYPRINT_OUTPUT_STREAM { output_stream, ... } ) # Flush any buffered output.
=
fil::flush output_stream;
fun close (PRETTYPRINT_OUTPUT_STREAM { output_stream, ... } ) #
=
fil::close_output output_stream;
# Enable traitful output by passing TRUE to this function.
# Return the previous state of the output stream:
fun texttrait_mode (PRETTYPRINT_OUTPUT_STREAM { stk => REF(_ ! _), ... }, _)
=>
raise exception DIE "attempt to change mode inside scope of texttraits";
texttrait_mode (PRETTYPRINT_OUTPUT_STREAM { mode, ... }, NULL)
=>
*mode;
texttrait_mode (PRETTYPRINT_OUTPUT_STREAM { mode as REF m, ... }, THE flag)
=>
{ mode := flag;
m;
};
end;
};
end;