1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: jan@swi-prolog.org 5 WWW: https://www.swi-prolog.org 6 Copyright (c) 2010-2026, VU University Amsterdam 7 CWI, Amsterdam 8 SWI-Prolog Solutions b.v. 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(ansi_term, 38 [ ansi_format/3, % +Attr, +Format, +Args 39 ansi_format/4, % +Stream, +Attr, +Format, +Args 40 ansi_sgr/2, % +Attr, -Sequence 41 ansi_get_color/2, % +Which, -rgb(R,G,B) 42 ansi_hyperlink/2, % +Stream,+Location 43 ansi_hyperlink/3 % +Stream,+URL,+Label 44 ]). 45:- autoload(library(error), [domain_error/2, must_be/2, instantiation_error/1]). 46:- autoload(library(lists), [append/3, selectchk/3]). 47:- autoload(library(utf8), [utf8_codes/3]). 48:- autoload(library(apply), [maplist/2]).
75:- multifile 76 prolog:console_color/2, % +Term, -AnsiAttrs 77 prolog:console_color/3, % +Theme, +Term, -AnsiAttrs 78 supports_get_color/0, 79 hyperlink/2, % +Stream, +Spec 80 tty_url_hook/2. % +For, -URL 81 82color_term_flag_default(true) :- 83 stream_property(user_input, tty(true)), 84 stream_property(user_error, tty(true)), 85 stream_property(user_output, tty(true)), 86 \+ getenv('TERM', dumb), 87 !. 88color_term_flag_default(false).
99hyperlink_term_flag_default(true) :- 100 color_term_flag_default(true), 101 \+ getenv('TMUX', _), 102 \+ getenv('STY', _), 103 osc8_terminal, 104 !. 105hyperlink_term_flag_default(false). 106 107osc8_terminal :- 108 getenv('TERM_PROGRAM', Program), 109 osc8_term_program(Program), 110 !. 111osc8_terminal :- 112 getenv('TERM', Term), 113 osc8_term(Term), 114 !. 115osc8_terminal :- 116 getenv('VTE_VERSION', Atom), % GNOME Terminal, Tilix, ... 117 atom_number(Atom, Version), 118 Version >= 5000, 119 !. 120osc8_terminal :- 121 getenv('KITTY_WINDOW_ID', _), 122 !. 123osc8_terminal :- 124 getenv('WT_SESSION', _). % Windows Terminal 125 126osc8_term_program('Epilog'). 127osc8_term_program('iTerm.app'). 128osc8_term_program('WezTerm'). 129osc8_term_program(ghostty). 130osc8_term_program(vscode). 131 132osc8_term('xterm-kitty'). 133osc8_term('xterm-ghostty'). 134osc8_term(foot). 135osc8_term('foot-extra'). 136osc8_term(wezterm). 137osc8_term(alacritty). 138 139init_color_term_flag :- 140 color_term_flag_default(Color), 141 create_prolog_flag(color_term, Color, 142 [ type(boolean), 143 keep(true) 144 ]), 145 hyperlink_term_flag_default(Hyperlink), 146 create_prolog_flag(hyperlink_term, Hyperlink, 147 [ type(boolean), 148 keep(true) 149 ]). 150 151:- initialization 152 init_color_term_flag. 153 154:- multifile 155 user:message_property/2.
current_output is a
terminal, it adds ANSI escape sequences according to Attributes.
For example, to print a text in bold cyan, do
?- ansi_format([bold,fg(cyan)], 'Hello ~w', [world]).
Attributes is either a single attribute, a list thereof or a term that is mapped to concrete attributes based on the current theme (see prolog:console_color/3). The attribute names are derived from the ANSI specification. See the source for sgr_code/2 for details. Some commonly used attributes are:
fg(Color) and bg(Color), the colour name can be '#RGB' or
'#RRGGBB'h(Color)
or an integer 0..255.
Defined color constants are below. default can be used to access
the default color of the terminal.
ANSI sequences are sent if and only if
current_output has the property tty(true) (see
stream_property/2).color_term is true.200ansi_format(Attr, Format, Args) :- 201 ansi_format(current_output, Attr, Format, Args). 202 203ansi_format(Stream, Class, Format, Args) :- 204 stream_property(Stream, tty(true)), 205 current_prolog_flag(color_term, true), 206 class_attrs(Class, Attr), 207 Attr \== [], 208 !, 209 ( selectchk(href(HREF), Attr, Attr1) 210 -> true 211 ; Attr1 = Attr 212 ), 213 sgr_sequence(Attr1, Sequence), 214 with_output_to( 215 Stream, 216 ( write(Sequence), 217 format_content(Format, Args, HREF), 218 format('\e[0m') 219 ) 220 ), 221 flush_output. 222ansi_format(Stream, _Attr, Format, Args) :- 223 format(Stream, Format, Args). 224 225format_content(Format, Args, HREF) :- 226 var(HREF), 227 !, 228 format(Format, Args). 229format_content(Format, Args, HREF) :- 230 format(string(Label), Format, Args), 231 ansi_hyperlink(current_output, HREF, Label).
color_term is false or the class
resolves to no attributes.
Unlike ansi_format/4 this does not write to a stream and thus does
not require a terminal. It is used to decorate strings that are
handed to code that is not aware of colors, notably the toplevel
prompt (see the Prolog flag toplevel_prompt). As the caller
controls where the sequence ends up, the caller is also responsible
for verifying that the destination is a terminal and for emitting
the reset sequence `\e[0m`.
250ansi_sgr(Class, Sequence) :- 251 current_prolog_flag(color_term, true), 252 class_attrs(Class, Attr), 253 Attr \== [], 254 ( selectchk(href(_), Attr, Attr1) 255 -> true 256 ; Attr1 = Attr 257 ), 258 !, 259 sgr_sequence(Attr1, Sequence). 260ansi_sgr(_, "").
bg8(Color).267sgr_sequence(Attrs, Sequence) :- 268 phrase(sgr_codes_ex(Attrs), Codes), 269 codes_sequence(Codes, Sequence). 270 271codes_sequence(Codes, Sequence) :- 272 atomics_to_string(Codes, ;, Code), 273 format(string(Sequence), '\e[~wm', [Code]).
281sgr_codes(X) --> 282 { var(X), 283 !, 284 fail 285 }. 286sgr_codes([]) --> 287 !. 288sgr_codes([H|T]) --> 289 !, 290 sgr_codes(H), 291 sgr_codes(T). 292sgr_codes(Attr) --> 293 { sgr_code(Attr, Code) }, 294 ( { is_list(Code) } 295 -> list(Code) 296 ; [Code] 297 ). 298 299sgr_codes_ex(X) --> 300 { var(X), 301 !, 302 instantiation_error(X) 303 }. 304sgr_codes_ex([]) --> 305 !. 306sgr_codes_ex([H|T]) --> 307 !, 308 sgr_codes_ex(H), 309 sgr_codes_ex(T). 310sgr_codes_ex(Attr) --> 311 ( { sgr_code(Attr, Code) } 312 -> ( { is_list(Code) } 313 -> list(Code) 314 ; [Code] 315 ) 316 ; { domain_error(sgr_code, Attr) } 317 ). 318 319list([]) --> []. 320list([H|T]) --> [H], list(T).
| reset | all attributes off |
| bold | |
| faint | |
| italic | |
| underline | |
blink(slow) | |
blink(rapid) | |
| negative | |
| conceal | |
| crossed_out | |
font(primary) | |
font(N) | Alternate font (1..8) |
| fraktur | |
underline(double) | |
intensity(normal) | |
fg(Name) | Color name |
bg(Name) | Color name |
| framed | |
| encircled | |
| overlined | |
ideogram(underline) | |
| right_side_line | |
ideogram(underline(double)) | |
right_side_line(double) | |
ideogram(overlined) | |
| left_side_line | |
ideogram(stress_marking) | |
| -Off | Switch attributes off |
hfg(Name) | Color name |
hbg(Name) | Color name |
362sgr_code(reset, 0). 363sgr_code(bold, 1). 364sgr_code(faint, 2). 365sgr_code(italic, 3). 366sgr_code(underline, 4). 367sgr_code(blink(slow), 5). 368sgr_code(blink(rapid), 6). 369sgr_code(negative, 7). 370sgr_code(conceal, 8). 371sgr_code(crossed_out, 9). 372sgr_code(font(primary), 10) :- !. 373sgr_code(font(N), C) :- 374 C is 10+N. 375sgr_code(fraktur, 20). 376sgr_code(underline(double), 21). 377sgr_code(intensity(normal), 22). 378sgr_code(fg(Name), C) :- 379 ( ansi_color(Name, N) 380 -> C is N+30 381 ; rgb(Name, R, G, B) 382 -> sgr_code(fg(R,G,B), C) 383 ). 384sgr_code(bg(Name), C) :- 385 !, 386 ( ansi_color(Name, N) 387 -> C is N+40 388 ; rgb(Name, R, G, B) 389 -> sgr_code(bg(R,G,B), C) 390 ). 391sgr_code(framed, 51). 392sgr_code(encircled, 52). 393sgr_code(overlined, 53). 394sgr_code(ideogram(underline), 60). 395sgr_code(right_side_line, 60). 396sgr_code(ideogram(underline(double)), 61). 397sgr_code(right_side_line(double), 61). 398sgr_code(ideogram(overlined), 62). 399sgr_code(left_side_line, 62). 400sgr_code(ideogram(stress_marking), 64). 401sgr_code(-X, Code) :- 402 off_code(X, Code). 403sgr_code(hfg(Name), C) :- 404 ansi_color(Name, N), 405 C is N+90. 406sgr_code(hbg(Name), C) :- 407 !, 408 ansi_color(Name, N), 409 C is N+100. 410sgr_code(fg8(Name), [38,5,N]) :- 411 ansi_color8(Name, N). 412sgr_code(bg8(Name), [48,5,N]) :- 413 ansi_color8(Name, N). 414sgr_code(fg(R,G,B), [38,2,R,G,B]) :- 415 between(0, 255, R), 416 between(0, 255, G), 417 between(0, 255, B). 418sgr_code(bg(R,G,B), [48,2,R,G,B]) :- 419 between(0, 255, R), 420 between(0, 255, G), 421 between(0, 255, B). 422 423off_code(italic_and_franktur, 23). 424off_code(underline, 24). 425off_code(blink, 25). 426off_code(negative, 27). 427off_code(conceal, 28). 428off_code(crossed_out, 29). 429off_code(framed, 54). 430off_code(overlined, 55). 431 432ansi_color8(h(Name), N) :- 433 !, 434 ansi_color(Name, N0), 435 N is N0+8. 436ansi_color8(Name, N) :- 437 atom(Name), 438 !, 439 ansi_color(Name, N). 440ansi_color8(N, N) :- 441 between(0, 255, N). 442 443ansi_color(black, 0). 444ansi_color(red, 1). 445ansi_color(green, 2). 446ansi_color(yellow, 3). 447ansi_color(blue, 4). 448ansi_color(magenta, 5). 449ansi_color(cyan, 6). 450ansi_color(white, 7). 451ansi_color(default, 9). 452 453rgb(Name, R, G, B) :- 454 atom_codes(Name, [0'#,R1,R2,G1,G2,B1,B2]), 455 hex_color(R1,R2,R), 456 hex_color(G1,G2,G), 457 hex_color(B1,B2,B). 458rgb(Name, R, G, B) :- 459 atom_codes(Name, [0'#,R1,G1,B1]), 460 hex_color(R1,R), 461 hex_color(G1,G), 462 hex_color(B1,B). 463 464hex_color(D1,D2,V) :- 465 code_type(D1, xdigit(V1)), 466 code_type(D2, xdigit(V2)), 467 V is 16*V1+V2. 468 469hex_color(D1,V) :- 470 code_type(D1, xdigit(V1)), 471 V is 16*V1+V1.
theme or, if no
theme is selected, the theme of a loaded theme file. Defaults
are defined in the file boot/messages.pl, default_theme/2.
Besides the classes used for messages (code, comment, var,
warning, error, truth(Truth), port(Port), message(Kind),
...), the interactive toplevel uses these:
?- prompt and its `| ` continuation.odd or even
and alternates over the answers of a single query, which allows
for striping the answers using a background color. Only an
answer that shows bindings, residual goals or delays uses this
class: true. and false. are not answers to stripe, and
neither is the empty line that separates the answer from the
next query.X = 1.
The debugger uses, besides frame(level) and port(Port):
odd or even
and alternates over the steps of a trace, which allows for
striping the goals using a background color. Match on Port to
color the goal by port instead of (or in addition to) striping.
Note that a background color on prompt, input or answer(_) is
painted up to the right margin using `\e[K`. A background on
goal(_,_) is not: the debugger writes its ? prompt on the same
line.
If Theme does not define Term, the theme it is based on is used (see prolog:theme_base/2).
:- module(prolog_theme_my_dark, []). :- use_module(library(theme/dark)). :- multifile prolog:theme/1, prolog:theme_base/2. prolog:theme(my_dark). prolog:theme_base(my_dark, dark).
543 /******************************* 544 * HOOK * 545 *******************************/
nl. Resetting matters because a
terminal that scrolls while a background colour is in effect
paints the newly exposed line with it.Ctx is the message context. It is created by the handler for begin/2 as a term
ansi(Reset, ReInstall, EraseEol)
where Reset is the sequence written by end/1, ReInstall is a
Format-Args pair that re-installs the attributes of the message and
EraseEol is the sequence that paints the remainder of the line or
the empty atom. Callers must treat Ctx as opaque. It is left
unbound if Stream is not a terminal, if the color_term flag is
false or if the message has no attributes. All handlers that use
Ctx therefore fail if it is unbound, which makes
print_message_lines/3 fall back to writing plain text.
590prolog:message_line_element(S, ansi(Class, Fmt, Args)) :- 591 class_attrs(Class, Attr), 592 ansi_format(S, Attr, Fmt, Args). 593prolog:message_line_element(S, ansi(Class, Fmt, Args, Ctx)) :- 594 class_attrs(Class, Attr), 595 ansi_format(S, Attr, Fmt, Args), 596 reinstall_message_attrs(S, Ctx). 597prolog:message_line_element(S, nl(Ctx)) :- 598 nonvar(Ctx), 599 Ctx = ansi(_, _, EOL), 600 write(S, EOL), 601 nl(S). 602prolog:message_line_element(S, flush(Ctx)) :- 603 nonvar(Ctx), 604 Ctx = ansi(_, _, EOL), 605 write(S, EOL), 606 flush_output(S). 607prolog:message_line_element(S, eol(Ctx)) :- 608 nonvar(Ctx), 609 Ctx = ansi(Reset, _, EOL), 610 write(S, EOL), 611 write(S, Reset). 612prolog:message_line_element(S, url(Location)) :- 613 ansi_hyperlink(S, Location). 614prolog:message_line_element(S, url(URL, Label)) :- 615 link_label(Label, Class, Fmt, Args, Ctx), 616 !, 617 format(string(Text), Fmt, Args), 618 ( ansi_sgr_for(S, Class, Sequence) 619 -> write(S, Sequence), 620 ansi_hyperlink(S, URL, Text), 621 write(S, '\e[0m'), 622 reinstall_message_attrs(S, Ctx) 623 ; ansi_hyperlink(S, URL, Text) 624 ). 625prolog:message_line_element(S, url(URL, Label)) :- 626 ansi_hyperlink(S, URL, Label). 627prolog:message_line_element(S, begin(Level, Ctx)) :- 628 level_attrs(Level, Attr), 629 Attr \== [], 630 stream_property(S, tty(true)), 631 current_prolog_flag(color_term, true), 632 phrase(sgr_codes(Attr), Codes), % fails on a kind without a theme 633 !, 634 codes_sequence(Codes, Sequence), 635 write(S, Sequence), 636 erase_eol(Attr, EOL), 637 Ctx = ansi('\e[0m', '\e[0m~w'-[Sequence], EOL). 638prolog:message_line_element(S, end(Ctx)) :- 639 nonvar(Ctx), 640 Ctx = ansi(Reset, _, _), 641 write(S, Reset).
648reinstall_message_attrs(S, Ctx) :-
649 ( nonvar(Ctx),
650 Ctx = ansi(_, RI-RA, _)
651 -> format(S, RI, RA)
652 ; true
653 ).660link_label(Fmt-Args, -, Fmt, Args, _) :- 661 atom(Fmt), 662 is_list(Args), 663 !. 664link_label(ansi(Class, Fmt, Args), Class, Fmt, Args, _). 665link_label(ansi(Class, Fmt, Args, Ctx), Class, Fmt, Args, Ctx).
673ansi_sgr_for(S, Class, Sequence) :-
674 Class \== (-),
675 stream_property(S, tty(true)),
676 ansi_sgr(Class, Sequence),
677 Sequence \== "".686erase_eol(Attrs, EOL) :- 687 ( is_list(Attrs), 688 has_background(Attrs) 689 -> EOL = '\e[K' 690 ; EOL = '' 691 ). 692 693has_background([H|T]) :- 694 ( background(H) 695 -> true 696 ; has_background(T) 697 ). 698 699background(bg(_)). 700background(bg(_,_,_)). 701background(bg8(_)). 702background(hbg(_)). 703 704level_attrs(Level, Attrs) :- 705 user:message_property(Level, color(Attrs)), 706 !. 707level_attrs(Level, Attrs) :- 708 class_attrs(message(Level), Attrs). 709 710class_attrs(Class, Attrs) :- 711 user:message_property(Class, color(Attrs)), 712 !. 713class_attrs(Class, Attrs) :- 714 prolog:console_color(Class, Attrs), 715 !. 716class_attrs(Class, Attrs) :- 717 '$messages':current_theme(Theme), 718 '$messages':theme_ancestor(Theme, Ancestor), 719 prolog:console_color(Ancestor, Class, Attrs), 720 !. 721class_attrs(Class, Attrs) :- 722 '$messages':default_theme(Class, Attrs), 723 !. 724class_attrs(Attrs, Attrs).
OSC 8
escape sequence. Location is one of
File:LineFile:Line:ColumnThere is no official standard for encoding the Line and Column. We emit
``file://AbsFileName[#Line[:Column]]``
Both Line and Column count from 1, as in the messages we
print and as used by e.g., rg --hyperlink-format=.... Note that
a capital L before the line, as used by GitHub, is accepted by
Epilog as well.
The sequence is emitted if and only if the Prolog flag
hyperlink_term is true and Stream has the property tty(true).
ansi_format/4 is guarded the same way for color_term, so that a
message captured using with_output_to/2 is plain text.
754ansi_hyperlink(Stream, Location) :- 755 hyperlink(Stream, url(Location)), 756 !. 757ansi_hyperlink(Stream, Location) :- 758 location_label(Location, Label), 759 ansi_hyperlink(Stream, Location, Label). 760 761location_label(File:Line:Column, Label) => 762 format(string(Label), '~w:~w:~w', [File,Line,Column]). 763location_label(File:Line, Label) => 764 format(string(Label), '~w:~w', [File,Line]). 765location_label(File, Label) => 766 format(string(Label), '~w', [File]). 767 768ansi_hyperlink(Stream, Location, Label), 769 hyperlink(Stream, url(Location, Label)) => 770 true. 771ansi_hyperlink(Stream, Location, Label) => 772 ( hyperlink_stream(Stream), 773 location_url(Location, URL) 774 -> format(Stream, '\e]8;;~w\e\\', [URL]), 775 format(Stream, '~w', [Label]), 776 format(Stream, '\e]8;;\e\\', []) 777 ; format(Stream, '~w', [Label]) 778 ).
785hyperlink_stream(Stream) :-
786 current_prolog_flag(hyperlink_term, true),
787 stream_property(Stream, tty(true)).794is_url(URL) :- 795 ( atom(URL) 796 -> true 797 ; string(URL) 798 ), 799 sub_string(URL, Before, _, _, :), 800 !, 801 Before > 0, 802 sub_string(URL, 0, Before, _, Scheme), 803 atom_codes(Scheme, Codes), 804 maplist(between(0'a, 0'z), Codes), 805 not_drive_scheme(Scheme). 806 807:- if(current_prolog_flag(windows, true)). 808not_drive_scheme(Scheme) :- 809 \+ string_length(Scheme, 1). 810:- else. 811not_drive_scheme(_). 812:- endif.
820location_url(Location, URL), 821 tty_url_hook(Location, URL0) => 822 URL = URL0. 823location_url(File:Line:Column, URL) => 824 url_file_name(FileURL, File), 825 format(string(URL), '~w#~d:~d', [FileURL, Line, Column]). 826location_url(File:Line, URL) => 827 url_file_name(FileURL, File), 828 format(string(URL), '~w#~w', [FileURL, Line]). 829location_url(File, URL) => 830 url_file_name(URL, File).
clib package and its foreign support.842url_file_name(URL, File) :- 843 is_url(File), !, 844 URL = File. 845url_file_name(URL, File) :- 846 absolute_file_name(File, AbsFile), 847 ensure_leading_slash(AbsFile, AbsFile1), 848 url_encode_path(AbsFile1, Encoded), 849 format(string(URL), 'file://~s', [Encoded]). 850 851ensure_leading_slash(Path, SlashPath) :- 852 ( sub_atom(Path, 0, _, _, /) 853 -> SlashPath = Path 854 ; atom_concat(/, Path, SlashPath) 855 ). 856 857url_encode_path(Name, Encoded) :- 858 atom_codes(Name, Codes), 859 phrase(utf8_codes(Codes), UTF8), 860 phrase(encode(UTF8), Encoded). 861 862encode([]) --> []. 863encode([H|T]) --> encode1(H), encode(T). 864 865encode1(C) --> 866 { reserved(C), 867 !, 868 format(codes([C1,C2]), '~`0t~16r~2|', [C]) 869 }, 870 "%", [C1,C2]. 871encode1(C) --> 872 [C]. 873 874reserved(C) :- C =< 0'\s. 875reserved(C) :- C >= 127. 876reserved(0'#).
foreground and background. This predicate sends a request to the
console (user_output) and reads the reply. This assumes an xterm
compatible terminal.
896ansi_get_color(Which0, RGB) :- 897 \+ current_prolog_flag(console_menu, true), 898 stream_property(user_input, tty(true)), 899 stream_property(user_output, tty(true)), 900 stream_property(user_error, tty(true)), 901 supports_get_color, 902 ( color_alias(Which0, Which) 903 -> true 904 ; must_be(between(0,15),Which0) 905 -> Which = Which0 906 ), 907 catch(ansi_get_color_(Which, RGB), 908 error(timeout_error(_,_), _), 909 no_xterm). 910 911supports_get_color :- 912 \+ current_prolog_flag(windows, true), 913 getenv('TERM', Term), 914 sub_atom(Term, 0, _, _, xterm), 915 \+ getenv('TERM_PROGRAM', 'Apple_Terminal'). 916 917color_alias(foreground, 10). 918color_alias(background, 11). 919 920ansi_get_color_(Which, RGB) :- 921 stream_property(user_input, timeout(Old)), 922 setup_call_cleanup( 923 set_stream(user_input, timeout(0.05)), 924 with_tty_raw(exchange_color(Which, RGB)), 925 set_stream(user_input, timeout(Old))), 926 !. 927 928no_xterm :- 929 print_message(warning, ansi(no_xterm_get_colour)), 930 fail.
936exchange_color(Which, RGB) :-
937 format(user_output, '\e]~w;?\a', [Which]),
938 flush_output(user_output),
939 format(codes(Id), '~w', [Which]),
940 read_osc_reply(user_input, Id, Codes),
941 phrase(color_reply(RGB), Codes).ESC ] Param ; and the string terminator. Terminals reply with
either BEL or ST (ESC \), so we accept both.
Anything that is not our reply is echoed: it was typed by the user while we were waiting rather than sent by the terminal.
952read_osc_reply(In, Param, Body) :- 953 get_code(In, C), 954 C \== -1, 955 ( C == 0'\e, 956 osc_reply(In, Param, Body0) 957 -> Body = Body0 958 ; put_code(user_output, C), 959 read_osc_reply(In, Param, Body) 960 ). 961 962osc_reply(In, Param, Body) :- 963 get_code(In, 0']), 964 read_osc_param(In, Param), 965 read_osc_string(In, Body). 966 967read_osc_param(In, Param) :- 968 read_osc_param_(In, Codes), 969 Codes == Param. 970 971read_osc_param_(In, Codes) :- 972 get_code(In, C), 973 C \== -1, 974 ( C == 0'; 975 -> Codes = [] 976 ; Codes = [C|T], 977 read_osc_param_(In, T) 978 ). 979 980read_osc_string(In, Codes) :- 981 get_code(In, C), 982 C \== -1, 983 ( C == 0'\a % BEL 984 -> Codes = [] 985 ; C == 0'\e % ST: ESC \ 986 -> get_code(In, 0'\\), 987 Codes = [] 988 ; Codes = [C|T], 989 read_osc_string(In, T) 990 ).
1001color_reply(rgb(R,G,B)) --> 1002 "rgb:", !, 1003 hex_component(R), "/", hex_component(G), "/", hex_component(B). 1004color_reply(rgb(R,G,B)) --> % #RGB, #RRGGBB, ... 1005 "#", 1006 hex_digits(Ds), 1007 { length(Ds, Len), 1008 N is Len//3, 1009 N > 0, N =< 4, 1010 length(RDs, N), length(GDs, N), length(BDs, N), 1011 append(RDs, Rest, Ds), 1012 append(GDs, BDs, Rest), 1013 hex_value(RDs, R), 1014 hex_value(GDs, G), 1015 hex_value(BDs, B) 1016 }. 1017 1018hex_component(V) --> 1019 hex_digits(Ds), 1020 { Ds \== [], 1021 length(Ds, N), 1022 N =< 4, 1023 hex_value(Ds, V) 1024 }. 1025 1026hex_digits([H|T]) --> 1027 [H], 1028 { code_type(H, xdigit(_)) }, 1029 !, 1030 hex_digits(T). 1031hex_digits([]) --> 1032 [].
prefer_rationals.1040hex_value(Ds, V) :- 1041 hex_value(Ds, 0, V0), 1042 length(Ds, N), 1043 Max is 16^N - 1, 1044 V is float(V0)/Max. 1045 1046hex_value([], V, V). 1047hex_value([D|T], V0, V) :- 1048 code_type(D, xdigit(DV)), 1049 V1 is V0*16+DV, 1050 hex_value(T, V1, V). 1051 1052:- multifile prolog:message//1. 1053 1054prologmessage(ansi(no_xterm_get_colour)) --> 1055 [ 'Terminal claims to be xterm compatible,'-[], nl, 1056 'but does not report colour info'-[] 1057 ]
Print decorated text to ANSI consoles
This library allows for exploiting the color and attribute facilities of most modern terminals using ANSI escape sequences. This library provides the following:
The behavior of this library is controlled by two Prolog flags:
true, activate the color output for this library. Otherwise simply call format/3.url(Location)andurl(URL, Label)elements of Prolog messages. The default istrueif the environment identifies a terminal known to support OSC 8 hyperlinks.