.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Text::ANSI::Util 3pm" .TH Text::ANSI::Util 3pm "2019-07-11" "perl v5.28.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Text::ANSI::Util \- Routines for text containing ANSI color codes .SH "VERSION" .IX Header "VERSION" This document describes version 0.230 of Text::ANSI::Util (from Perl distribution Text-ANSI-Util), released on 2019\-04\-22. .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 10 \& use Text::ANSI::Util qw( \& ta_add_color_resets \& ta_detect \& ta_extract_codes \& ta_highlight \& ta_highlight_all \& ta_length \& ta_length_height \& ta_pad \& ta_split_codes \& ta_split_codes_single \& ta_strip \& ta_substr \& ta_trunc \& ta_wrap \& ); \& \& # detect whether text has ANSI color codes? \& say ta_detect("red"); # => false \& say ta_detect("\ee[31mred"); # => true \& \& # calculate length of text (excluding the ANSI color codes) \& say ta_length("red"); # => 3 \& say ta_length("\ee[31mred"); # => 3 \& \& # strip ANSI color codes \& say ta_strip("\ee[31mred"); # => "red" \& \& # split codes (ANSI color codes are always on the even positions) \& my @parts = ta_split_codes("\ee[31mred"); # => ("", "\ee[31m", "red") \& \& # wrap text to a certain column width, handle ANSI color codes \& say ta_wrap("....", 40); \& \& # pad (left, right, center) text to a certain width \& say ta_pad("foo", 10); # => "foo " \& say ta_pad("foo", 10, "left"); # => " foo" \& say ta_pad("foo\enbarbaz\en", 10, "center", "."); # => "...foo....\en..barbaz..\en" \& \& # truncate text to a certain width while still passing ANSI color codes \& use Term::ANSIColor; \& my $text = color("red")."red text".color("reset"); # => "\ee[31mred text\ee[0m" \& say ta_trunc($text, 5); # => "\ee[31mred t\ee[0m" \& \& # highlight the first occurrence of some string within text \& say ta_highlight("some text", "ome", "\ee[7m\ee[31m"); \& \& # ditto, but highlight all occurrences \& say ta_highlight_all(...); \& \& # get substring \& my $substr = ta_substr("...", $pos, $len); \& \& # return text but with substring replaced with replacement \& say ta_substr("...", $pos, $len, $replacement); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module provides routines for dealing with text that contains \s-1ANSI\s0 color codes, e.g. for determining its length/width (excluding the color codes), stripping the color codes, extracting the color codes, and so on. .PP For functions that support wide characters, see Text::ANSI::WideUtil. .PP Current caveats: .IP "\(bu" 4 Other \s-1ANSI\s0 codes (non-color codes) are ignored .Sp These are codes like for altering cursor positions, etc. .IP "\(bu" 4 Single-character \s-1CSI\s0 (control sequence introducer) currently ignored .Sp Only \f(CW\*(C`ESC+[\*(C'\fR (two-character \s-1CSI\s0) is currently parsed. .Sp \&\s-1BTW,\s0 in \s-1ASCII\s0 terminals, single-character \s-1CSI\s0 is \f(CW0x9b\fR. In \s-1UTF\-8\s0 terminals, it is \f(CW\*(C`0xc2, 0x9b\*(C'\fR (2 bytes). .IP "\(bu" 4 Private\-mode\- and trailing-intermediate character currently not parsed .IP "\(bu" 4 Only color reset code \ee[0m is recognized .Sp For simplicity, currently multiple \s-1SGR\s0 parameters inside a single \s-1ANSI\s0 color code is not parsed. This means that color reset code like \f(CW\*(C`\ee[1;0m\*(C'\fR or \&\f(CW\*(C`\ee[31;47;0m\*(C'\fR is not recognized, only \f(CW\*(C`\ee[0m\*(C'\fR is. I believe this should not be a problem with most real-world text out there. .SH "FUNCTIONS" .IX Header "FUNCTIONS" .SS "ta_add_color_resets(@text) => \s-1LIST\s0" .IX Subsection "ta_add_color_resets(@text) => LIST" Make sure that a color reset command (add \f(CW\*(C`\ee[0m\*(C'\fR) to the end of each element and a replay of all the color codes from the previous element, from the last color reset) to the start of the next element, and so on. Return the new list. .PP This makes each element safe to be combined with other array of text into a single line, e.g. in a multicolumn/tabular layout. An example: .PP Without color resets: .PP .Vb 2 \& my @col1 = split /\en/, "\ee[31mred\enmerah\ee[0m"; \& my @col2 = split /\en/, "\ee[32mgreen\ee[1m\enhijau tebal\ee[0m"; \& \& printf "%s | %s\en", $col1[0], $col2[0]; \& printf "%s | %s\en", $col1[1], $col2[1]; .Ve .PP the printed output: .PP .Vb 2 \& \ee[31mred | \ee[32mgreen \& merah\ee[0m | \ee[1mhijau tebal\ee[0m .Ve .PP The \f(CW\*(C`merah\*(C'\fR text on the second line will become green because of the effect of the last color command printed (\f(CW\*(C`\ee[32m\*(C'\fR). However, with \fBta_add_color_resets()\fR: .PP .Vb 2 \& my @col1 = ta_add_color_resets(split /\en/, "\ee[31mred\enmerah\ee[0m"); \& my @col2 = ta_add_color_resets(split /\en/, "\ee[32mgreen\ee[1m\enhijau tebal\ee[0m"); \& \& printf "%s | %s\en", $col1[0], $col2[0]; \& printf "%s | %s\en", $col1[1], $col2[1]; .Ve .PP the printed output (\f(CW\*(C`<...>\*(C'\fR) marks the code added by \fBta_add_color_resets()\fR: .PP .Vb 2 \& \ee[31mred<\ee[0m> | \ee[32mgreen\ee[1m<\ee[0m> \& <\ee[31m>merah\ee[0m | <\ee[32m\ee[1m>hijau tebal\ee[0m .Ve .PP All the cells are printed with the intended colors. .SS "ta_detect($text) => \s-1BOOL\s0" .IX Subsection "ta_detect($text) => BOOL" Return true if \f(CW$text\fR contains \s-1ANSI\s0 color codes, false otherwise. .SS "ta_extract_codes($text) => \s-1STR\s0" .IX Subsection "ta_extract_codes($text) => STR" This is the opposite of \f(CW\*(C`ta_strip()\*(C'\fR, return only the \s-1ANSI\s0 codes in \f(CW$text\fR. .ie n .SS "ta_highlight($text, $needle, $color) => \s-1STR\s0" .el .SS "ta_highlight($text, \f(CW$needle\fP, \f(CW$color\fP) => \s-1STR\s0" .IX Subsection "ta_highlight($text, $needle, $color) => STR" Highlight the first occurrence of \f(CW$needle\fR in \f(CW$text\fR with <$color>, taking care not to mess up existing colors. .PP \&\f(CW$needle\fR can be a string or a Regexp object. .PP Implementation note: to not mess up colors, we save up all color codes from the last reset (\f(CW\*(C`\ee[0m\*(C'\fR) before inserting the highlight color + highlight text. Then we issue \f(CW\*(C`\ee[0m\*(C'\fR and the saved up color code to return back to the color state before the highlight is inserted. This is the same technique as described in \f(CW\*(C`ta_add_color_resets()\*(C'\fR. .ie n .SS "ta_highlight_all($text, $needle, $color) => \s-1STR\s0" .el .SS "ta_highlight_all($text, \f(CW$needle\fP, \f(CW$color\fP) => \s-1STR\s0" .IX Subsection "ta_highlight_all($text, $needle, $color) => STR" Like \f(CW\*(C`ta_highlight()\*(C'\fR, but highlight all occurrences instead of only the first. .SS "ta_length($text) => \s-1INT\s0" .IX Subsection "ta_length($text) => INT" Count the number of characters in \f(CW$text\fR, while ignoring \s-1ANSI\s0 color codes. Equivalent to \f(CW\*(C`length(ta_strip($text))\*(C'\fR. See also: \f(CW\*(C`ta_mbswidth()\*(C'\fR in Text::ANSI::WideUtil. .SS "ta_length_height($text) => [\s-1INT, INT\s0]" .IX Subsection "ta_length_height($text) => [INT, INT]" Like \f(CW\*(C`ta_length()\*(C'\fR, but also gives height (number of lines). For example, \f(CW\*(C`ta_length_height("foobar\enb\en")\*(C'\fR gives \f(CW\*(C`[6, 3]\*(C'\fR. .PP See also: \f(CW\*(C`ta_mbswidth_height()\*(C'\fR in Text::ANSI::WideUtil. .ie n .SS "ta_pad($text, $width[, $which[, $padchar[, $truncate]]]) => \s-1STR\s0" .el .SS "ta_pad($text, \f(CW$width\fP[, \f(CW$which\fP[, \f(CW$padchar\fP[, \f(CW$truncate\fP]]]) => \s-1STR\s0" .IX Subsection "ta_pad($text, $width[, $which[, $padchar[, $truncate]]]) => STR" Return \f(CW$text\fR padded with \f(CW$padchar\fR to \f(CW$width\fR columns. \f(CW$which\fR is either \*(L"r\*(R" or \*(L"right\*(R" for padding on the right (the default if not specified), \&\*(L"l\*(R" or \*(L"left\*(R" for padding on the right, or \*(L"c\*(R" or \*(L"center\*(R" or \*(L"centre\*(R" for left+right padding to center the text. .PP \&\f(CW$padchar\fR is whitespace if not specified. It should be string having the width of 1 column. .PP Does *not* handle multiline text; you can split text by \f(CW\*(C`/\er?\en/\*(C'\fR yourself. .PP See also: \f(CW\*(C`ta_mbpad()\*(C'\fR in Text::ANSI::WideUtil. .SS "ta_split_codes($text) => \s-1LIST\s0" .IX Subsection "ta_split_codes($text) => LIST" Split \f(CW$text\fR to a list containing alternating \s-1ANSI\s0 color codes and text. \s-1ANSI\s0 color codes are always on the second element, fourth, and so on. Example: .PP .Vb 7 \& ta_split_codes(""); # => () \& ta_split_codes("a"); # => ("a") \& ta_split_codes("a\ee[31m"); # => ("a", "\ee[31m") \& ta_split_codes("\ee[31ma"); # => ("", "\ee[31m", "a") \& ta_split_codes("\ee[31ma\ee[0m"); # => ("", "\ee[31m", "a", "\ee[0m") \& ta_split_codes("\ee[31ma\ee[0mb"); # => ("", "\ee[31m", "a", "\ee[0m", "b") \& ta_split_codes("\ee[31m\ee[0mb"); # => ("", "\ee[31m\ee[0m", "b") .Ve .PP so you can do something like: .PP .Vb 4 \& my @parts = ta_split_codes($text); \& while (my ($text, $ansicode) = splice(@parts, 0, 2)) { \& ... \& } .Ve .SS "ta_split_codes_single($text) => \s-1LIST\s0" .IX Subsection "ta_split_codes_single($text) => LIST" Like \f(CW\*(C`ta_split_codes()\*(C'\fR but each \s-1ANSI\s0 color code is split separately, instead of grouped together. This routine is currently used internally e.g. for \&\f(CW\*(C`ta_wrap()\*(C'\fR and \f(CW\*(C`ta_highlight()\*(C'\fR to trace color reset/replay codes. .SS "ta_strip($text) => \s-1STR\s0" .IX Subsection "ta_strip($text) => STR" Strip \s-1ANSI\s0 color codes from \f(CW$text\fR, returning the stripped text. .ie n .SS "ta_substr($text, $pos, $len[ , $replacement ]) => \s-1STR\s0" .el .SS "ta_substr($text, \f(CW$pos\fP, \f(CW$len\fP[ , \f(CW$replacement\fP ]) => \s-1STR\s0" .IX Subsection "ta_substr($text, $pos, $len[ , $replacement ]) => STR" A bit like Perl's \f(CW\*(C`substr()\*(C'\fR. If \f(CW$replacement\fR is not specified, will return the substring. If \f(CW$replacement\fR is specified, will return \f(CW$text\fR with the substring replaced by \f(CW$replacement\fR. .PP See also: \f(CW\*(C`ta_mbsubstr()\*(C'\fR in Text::ANSI::WideUtil. .ie n .SS "ta_trunc($text, $width) => \s-1STR\s0" .el .SS "ta_trunc($text, \f(CW$width\fP) => \s-1STR\s0" .IX Subsection "ta_trunc($text, $width) => STR" Truncate \f(CW$text\fR to \f(CW$width\fR columns while still including all the \s-1ANSI\s0 color codes. This ensures that truncated text still reset colors, etc. .PP Does *not* handle multiline text; you can split text by \f(CW\*(C`/\er?\en/\*(C'\fR yourself. .PP See also: \f(CW\*(C`ta_mbtrunc()\*(C'\fR in Text::ANSI::WideUtil. .ie n .SS "ta_wrap($text, $width, \e%opts) => \s-1STR\s0" .el .SS "ta_wrap($text, \f(CW$width\fP, \e%opts) => \s-1STR\s0" .IX Subsection "ta_wrap($text, $width, %opts) => STR" Like Text::WideChar::Util's \f(CW\*(C`wrap()\*(C'\fR except handles \s-1ANSI\s0 color codes. Perform color reset at the end of each line and a color replay at the start of subsequent line so the text is safe for combining in a multicolumn/tabular layout. .PP Options: .IP "\(bu" 4 flindent => \s-1STR\s0 .Sp First line indent. See Text::WideChar::Util for more details. .IP "\(bu" 4 slindent => \s-1STR\s0 .Sp First line indent. See Text::WideChar::Util for more details. .IP "\(bu" 4 tab_width => \s-1INT\s0 (default: 8) .Sp First line indent. See Text::WideChar::Util for more details. .IP "\(bu" 4 pad => \s-1BOOL\s0 (default: 0) .Sp If set to true, will pad each line to \f(CW$width\fR. This is convenient if you need the lines padded, saves calls to \fBta_pad()\fR. .IP "\(bu" 4 return_stats => \s-1BOOL\s0 (default: 0) .Sp If set to true, then instead of returning the wrapped string, function will return \f(CW\*(C`[$wrapped, $stats]\*(C'\fR where \f(CW$stats\fR is a hash containing some information like \f(CW\*(C`max_word_width\*(C'\fR, \f(CW\*(C`min_word_width\*(C'\fR. .PP Performance: ~500/s on my Core i5 1.7GHz laptop for a ~1KB of text (with zero to moderate amount of color codes). .PP See also: \f(CW\*(C`ta_mbwrap()\*(C'\fR in Text::ANSI::WideUtil. .SH "FAQ" .IX Header "FAQ" .SS "How do I highlight a string case-insensitively?" .IX Subsection "How do I highlight a string case-insensitively?" You can currently use a regex for the \f(CW$needle\fR and use the \f(CW\*(C`i\*(C'\fR modifier. Example: .PP .Vb 2 \& use Term::ANSIColor; \& ta_highlight($text, qr/\eb(foo)\eb/i, color("bold red")); .Ve .SH "HOMEPAGE" .IX Header "HOMEPAGE" Please visit the project's homepage at . .SH "SOURCE" .IX Header "SOURCE" Source repository is at . .SH "BUGS" .IX Header "BUGS" Please report any bugs or feature requests on the bugtracker website .PP When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. .SH "SEE ALSO" .IX Header "SEE ALSO" Text::ANSI::WideUtil .SH "AUTHOR" .IX Header "AUTHOR" perlancar .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is copyright (c) 2019, 2016, 2015, 2014, 2013 by perlancar@cpan.org. .PP This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.