@@ -86,7 +86,7 @@ that should handle it. `io_ansi:fwrite/4` works across nodes and will use the
8686
8787-export ([tput /1 , tput /2 , tigetnum /1 , tigetflag /1 , tinfo /0 ]).
8888-export ([format /1 , format /2 , format /3 , fwrite /1 , fwrite /2 , fwrite /3 , fwrite /4 ,
89- enabled /0 , enabled /1 , scan /1 ]).
89+ enabled /0 , enabled /1 , scan /1 , escape / 1 , escape / 2 ]).
9090
9191-export ([black /0 , blue /0 , cyan /0 , green /0 , magenta /0 , red /0 , white /0 , yellow /0 ,
9292 color /1 , color /3 , default_color /0 ]).
@@ -2243,67 +2243,51 @@ lookup_vts(Data) ->
22432243 KeyValueRest
22442244 end .
22452245
2246- -doc #{ equiv => format (Format , []) }.
2247- -spec format (format ()) -> unicode :unicode_binary ().
2248- format (Format ) ->
2249- format (Format , []).
2250-
2251- -doc #{ equiv => format (Format , Data , []) }.
2252- -spec format (format (), Data :: [term ()]) -> unicode :unicode_binary ().
2253- format (Format , Data ) ->
2254- format (Format , Data , []).
2246+ -doc #{ equiv => escape (Data , []) }.
2247+ -spec escape ([vts () | unicode :chardata ()]) -> unicode :chardata ().
2248+ escape (Data ) ->
2249+ escape (Data , []).
22552250
22562251-doc """
2257- Returns a character list that represents ` Data ` formatted in accordance with
2258- ` Format ` .
2252+ Escapes terminal sequences in ` Data ` .
22592253
2260- This function works just as ` io_lib:bformat/2 ` , except that it also allows
2261- atoms and tuples represeting virtual terminal sequences as part of the
2262- ` Format ` string.
2254+ ` Data ` may contain either virtual terminal sequences, which are escaped,
2255+ or ` unicode:chardata() ` , which are left as is.
22632256
2264- Calling ` format/3 ` will always emit a ` reset/0 ` VTS at the end of the returned
2265- string. To not emit this, set the ` reset ` option to ` false ` .
2266-
2267- To force enabling or disabling of emitting VTSs set the ` enabled ` option to
2268- ` true ` or ` false ` . By default the emitting of VTSs is enabled if ` enabled/0 ` returns ` true `
2269- and disabled otherwise.
2270-
2271- To disable emitting of color VTSs but still emit other VTSs, set the ` color ` option to ` false ` .
2272- The default color option is ` true ` unless the ` NO_COLOR ` environment variable is set to a non-empty value,
2273- in which case the default is ` false ` .
2257+ It accepts the same options as ` format/3 ` .
22742258
22752259Example:
22762260
22772261``` erlang
2278- 1 > io_ansi :format ([blue , underline , " Hello world" ]).
2279- ~ " \e [34m\e [4mHello world\e (B\e [m"
2280- 2 > io_ansi :format ([blue , underline , " Hello ~p " ],[world ]).
2281- ~ " \e [34m\e [4mHello world\e (B\e [m"
2282- 3 > io_ansi :format ([blue , underline , " Hello ~p " ],[world ],[{reset ,false }]).
2283- ~ " \e [34m\e [4mHello world"
2284- 4 > io_ansi :format ([blue , underline , " Hello ~p " ],[world ],[{enabled ,false }]).
2285- ~ " Hello world"
2286- 5 > io_ansi :format ([blue , underline , " Hello ~p " ],[world ],[{color ,false }]).
2287- ~ " \e [4mHello world\e (B\e [m"
2288- 6 > io_ansi :format ([invalid_code , " Hello world" ]).
2262+ 1 > io_ansi :escape ([blue , underline , " Hello world" ]).
2263+ [~ " \e [34m" ,~ " \e [4m" ," Hello world" ,~ " \e (B\e [m" ]
2264+ 2 > io_ansi :escape ([blue , underline , ~ " Hello world" ],[{reset ,false }]).
2265+ [~ " \e [34m" ,~ " \e [4m" ,~ " Hello world" ]
2266+ 3 > io_ansi :escape ([blue , underline , ~ " Hello world" ],[{enabled ,false }]).
2267+ [~ " Hello world" ]
2268+ 4 > io_ansi :escape ([blue , underline , " Hello " , $\n , ~ " world" ],[{color ,false }]).
2269+ [~ " \e [4m" ," Hello " , $\n , ~ " world" ,~ " \e (B\e [m" ]
2270+ 5 > io_ansi :escape ([invalid_code , " Hello world" ]).
22892271** exception error : {invalid_code ,invalid_code }
2290- in function io_ansi :format_internal / 3
2272+ in function io_ansi :escape_internal / 5
22912273```
2292-
2293- For a detailed description of the available formatting options, see ` io:fwrite/3 ` .
22942274""" .
2295- -spec format (format (), Data :: [term ()], options ()) -> unicode :unicode_binary ().
2296- format (Format , Data , Options ) ->
2297- format_internal (Format , Data , Options ).
2275+ -spec escape ([vts () | unicode :chardata ()], options ()) -> unicode :chardata ().
2276+ escape (Data , Options ) ->
2277+ escape_internal (Data , [], Options , false , fun
2278+ (Fmt , []) when is_list (Fmt ); is_binary (Fmt ); is_integer (Fmt ) ->
2279+ {Fmt , []};
2280+
2281+ (_ , _ ) ->
2282+ erlang :error (badarg , [Data , Options ])
2283+ end ).
22982284
2299- format_internal (Format , Data , Options ) ->
2285+ escape_internal (Format , InitAcc , Options , FormatOnly , Callback ) ->
23002286 UseAnsi = case proplists :get_value (enabled , Options ) of
23012287 undefined -> enabled ();
23022288 Enabled -> Enabled
23032289 end ,
2304- % % Only to be used by fwrite
2305- FormatOnly = proplists :get_value (format_only , Options , false ),
2306- AppendReset = [reset || proplists :get_value (reset , Options , true )],
2290+ AppendReset = proplists :get_value (reset , Options , true ),
23072291 NoColor = os :getenv (" NO_COLOR" ),
23082292 DefaultColor = NoColor =:= false orelse NoColor =:= " " ,
23092293 Color = proplists :get_value (color , Options , DefaultColor ),
@@ -2323,20 +2307,19 @@ format_internal(Format, Data, Options) ->
23232307 not RenderAnsi ->
23242308 {Acc , Args }
23252309 end ;
2326- (Ansi , {Acc , Args }) when is_atom (Ansi ); is_tuple (Ansi ) ->
2327- {Acc , Args };
23282310 (Fmt , {Acc , Args }) ->
2329- {Scanned , Rest } = io_lib_format : scan (Fmt , Args ),
2330- {[io_lib_format : build_bin ( Scanned ) | Acc ], Rest }
2331- end , {[], Data }, group ([ Format , AppendReset ]) ) of
2311+ {Entry , Rest } = Callback (Fmt , Args ),
2312+ {[Entry | Acc ], Rest }
2313+ end , {[], InitAcc }, Format ) of
23322314 {Scanned , []} ->
2333- if FormatOnly ->
2334- lists :flatten (lists :reverse (Scanned ));
2335- not FormatOnly ->
2336- unicode :characters_to_binary (lists :reverse (Scanned ))
2337- end ;
2315+ Result = if
2316+ UseAnsi andalso AppendReset -> [reset () | Scanned ];
2317+ true -> Scanned
2318+ end ,
2319+
2320+ lists :reverse (Result );
23382321 _ ->
2339- erlang :error (badarg , [Format , Data , Options ])
2322+ erlang :error (badarg , [Format , InitAcc , Options ])
23402323 catch throw :{invalid_code , Code , []} ->
23412324 erlang :error ({invalid_code , Code });
23422325 throw :{invalid_code , Code , Args } ->
@@ -2355,6 +2338,67 @@ is_color(AnsiKey) ->
23552338 lists :member (AnsiKey , ColorAtoms ++ [color , background_color , underline_color ,
23562339 default_color , default_background , default_underline_color ]).
23572340
2341+ - doc #{ equiv => format (Format , []) }.
2342+ - spec format (format ()) -> unicode :unicode_binary ().
2343+ format (Format ) ->
2344+ format (Format , []).
2345+
2346+ - doc #{ equiv => format (Format , Data , []) }.
2347+ - spec format (format (), Data :: [term ()]) -> unicode :unicode_binary ().
2348+ format (Format , Data ) ->
2349+ format (Format , Data , []).
2350+
2351+ - doc """
2352+ Returns a character list that represents `Data` formatted in accordance with
2353+ `Format`.
2354+
2355+ This function works just as `io_lib:bformat/2`, where `Data` is a list of strings
2356+ as well as atoms and tuples representing virtual terminal sequences as part of the
2357+ `Format` string.
2358+
2359+ Calling `format/3` will always emit a `reset/0` VTS at the end of the returned
2360+ string. To not emit this, set the `reset` option to `false`.
2361+
2362+ To force enabling or disabling of emitting VTSs set the `enabled` option to
2363+ `true` or `false`. By default the emitting of VTSs is enabled if `enabled/0` returns `true`
2364+ and disabled otherwise.
2365+
2366+ To disable emitting of color VTSs but still emit other VTSs, set the `color` option to `false`.
2367+ The default color option is `true` unless the `NO_COLOR` environment variable is set to a non-empty value,
2368+ in which case the default is `false`.
2369+
2370+ Example:
2371+
2372+ ```erlang
2373+ 1> io_ansi:format([blue, underline, "Hello world"]).
2374+ ~ "\e[34m\e[4mHello world\e(B\e[m"
2375+ 2> io_ansi:format([blue, underline, "Hello ~p "],[world]).
2376+ ~ "\e[34m\e[4mHello world\e(B\e[m"
2377+ 3> io_ansi:format([blue, underline, "Hello ~p "],[world],[{reset,false}]).
2378+ ~ "\e[34m\e[4mHello world"
2379+ 4> io_ansi:format([blue, underline, "Hello ~p "],[world],[{enabled,false}]).
2380+ ~ "Hello world"
2381+ 5> io_ansi:format([blue, underline, "Hello ~p "],[world],[{color,false}]).
2382+ ~ "\e[4mHello world\e(B\e[m"
2383+ ```
2384+
2385+ For a detailed description of the available formatting options, see `io:fwrite/3`.
2386+ """ .
2387+ - spec format (format (), Data :: [term ()], options ()) -> unicode :unicode_binary ().
2388+ format (Format , Data , Options ) ->
2389+ Formatted = format_internal (group (Format ), Data , Options , false ),
2390+ unicode :characters_to_binary (Formatted ).
2391+
2392+ format_internal (Format , Data , Options , FormatOnly ) ->
2393+ escape_internal (group (Format ), Data , Options , FormatOnly , fun
2394+ (Fmt , Args ) when is_list (Fmt ) ->
2395+ {Scanned , Rest } = io_lib_format :scan (Fmt , Args ),
2396+ {io_lib_format :build_bin (Scanned ), Rest };
2397+
2398+ (_ , _ ) ->
2399+ erlang :error (badarg , [Format , Data , Options ])
2400+ end ).
2401+
23582402- doc #{ equiv => fwrite (standard_io , Format , [], []) }.
23592403- spec fwrite (Format :: format ()) -> ok .
23602404fwrite (Format ) ->
23892433ok
239024343> io_ansi:fwrite([invalid_code, "%% Hello ~p \n"], [world]).
23912435** exception error: {error,{put_ansi,unicode,invalid_code}}
2392- in function io_ansi :fwrite / 4
2393- called as io_ansi :fwrite (standard_io ,[invalid_code ," %% Hello ~p \n " ],[world ],[])
23942436```
23952437
23962438The decision what each VTS should be converted to is done by the destination I/O
@@ -2428,7 +2470,7 @@ fwrite(Device, Format, Data, Options) ->
24282470 end ;
24292471 F (_Data , Error ) ->
24302472 throw ({Ref , Error })
2431- end , ok , format_internal (Format , Data , [{ format_only , true } | Options ] ))
2473+ end , ok , lists : flatten ( format_internal (Format , Data , Options , true ) ))
24322474 catch {Ref , Error } ->
24332475 erlang :error (Error , [Device , Format , Data , Options ])
24342476 end .
0 commit comments