PreviousUpNext

15.4.835  src/lib/prettyprint/big/src/out/ansi-terminal-prettyprint-output-stream.pkg

## 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.pkg
herein

    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;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext