File Coverage

blib/lib/Indent/Word.pm
Criterion Covered Total %
statement 79 82 96.3
branch 27 30 90.0
condition 11 12 91.6
subroutine 11 11 100.0
pod 2 2 100.0
total 130 137 94.8


line stmt bran cond sub pod time code
1             package Indent::Word;
2              
3 4     4   166195 use strict;
  4         7  
  4         121  
4 4     4   27 use warnings;
  4         5  
  4         169  
5              
6 4     4   1809 use Class::Utils qw(set_params);
  4         45040  
  4         1683  
7 4     4   318 use English qw(-no_match_vars);
  4         9  
  4         18  
8 4     4   1210 use Error::Pure qw(err);
  4         7  
  4         165  
9 4     4   1922 use Indent::Utils qw(line_size_check);
  4         9  
  4         209  
10 4     4   19 use Readonly;
  4         6  
  4         4010  
11              
12             # Constants.
13             Readonly::Scalar my $EMPTY_STR => q{};
14             Readonly::Scalar my $LINE_SIZE => 79;
15              
16             our $VERSION = 0.09;
17              
18             # Constructor.
19             sub new {
20 13     13 1 556756 my ($class, @params) = @_;
21 13         28 my $self = bless {}, $class;
22              
23             # Use with ANSI sequences.
24 13         44 $self->{'ansi'} = undef;
25              
26             # Options.
27 13         33 $self->{'line_size'} = $LINE_SIZE;
28 13         22 $self->{'next_indent'} = "\t";
29              
30             # Output.
31 13         53 $self->{'output_separator'} = "\n";
32              
33             # Process params.
34 13         53 set_params($self, @params);
35              
36             # 'line_size' check.
37 11         204 line_size_check($self);
38              
39 11 100       26 if (! defined $self->{'ansi'}) {
40 1 50       5 if (exists $ENV{'NO_COLOR'}) {
    50          
41 0         0 $self->{'ansi'} = 0;
42             } elsif (defined $ENV{'COLOR'}) {
43 0         0 $self->{'ansi'} = 1;
44             } else {
45 1         2 $self->{'ansi'} = 0;
46             }
47             }
48              
49             # Check rutine for removing ANSI sequences.
50 11 100       24 if ($self->{'ansi'}) {
51 4         5 eval {
52 4         37 require Text::ANSI::Util;
53             };
54 4 50       8 if ($EVAL_ERROR) {
55 0         0 err "Cannot load 'Text::ANSI::Util' module.";
56             }
57             }
58              
59             # Object.
60 11         39 return $self;
61             }
62              
63             # Indent text by words to line_size block size.
64             sub indent {
65 13     13 1 3086 my ($self, $data, $indent, $non_indent) = @_;
66              
67             # 'indent' initialization.
68 13 100       25 if (! defined $indent) {
69 4         6 $indent = $EMPTY_STR;
70             }
71              
72             # If non_indent data, than return.
73 13 100       18 if ($non_indent) {
74 1         5 return $indent.$data;
75             }
76              
77 12         29 my ($first, $second) = (undef, $indent.$data);
78 12         15 my $last_second_length = 0;
79 12         11 my @data;
80 12         12 my $one = 1;
81 12   100     25 while ($self->_length($second) >= $self->{'line_size'}
      100        
82             && $second =~ /^\s*\S+\s+/ms
83             && $last_second_length != $self->_length($second)) {
84              
85             # Last length of non-parsed part of data.
86 21         126 $last_second_length = $self->_length($second);
87              
88             # Parse to indent length.
89 21         116 ($first, my $tmp) = $self->_parse_to_indent_length($second);
90              
91             # If string is non-breakable in indent length, than parse to
92             # blank char.
93 21 100 100     55 if (! $first
      66        
94             || $self->_length($first) < $self->_length($indent)
95             || $first =~ /^$indent\s*$/ms) {
96              
97 11         516 ($first, $tmp) = $second
98             =~ /^($indent\s*[^\s]+?)\s(.*)$/msx;
99             }
100              
101             # If parsing is right.
102 21 100       132 if ($tmp) {
103              
104             # Non-parsed part of data.
105 16         16 $second = $tmp;
106              
107             # Add next_indent to string.
108 16 100       36 if ($one == 1) {
109 8         14 $indent .= $self->{'next_indent'};
110             }
111 16         18 $one = 0;
112 16         27 $second = $indent.$second;
113              
114             # Parsed part of data to @data array.
115 16         94 push @data, $first;
116             }
117             }
118              
119             # Add other data to @data array.
120 12         56 $second =~ s/\s+$//ms;
121 12 100       57 if ($second) {
122 10         17 push @data, $second;
123             }
124              
125             # Return as array or one line with output separator between its.
126 12 100       61 return wantarray ? @data : join($self->{'output_separator'}, @data);
127             }
128              
129             # Get length.
130             sub _length {
131 108     108   404 my ($self, $string) = @_;
132 108 100       137 if ($self->{'ansi'}) {
133 46         64 return length Text::ANSI::Util::ta_strip($string);
134             } else {
135 62         244 return length $string;
136             }
137             }
138              
139             # Parse to indent length.
140             sub _parse_to_indent_length {
141 21     21   27 my ($self, $string) = @_;
142 21         22 my @ret;
143 21 100       28 if ($self->{'ansi'}) {
144 9         13 my $string_wo_ansi = Text::ANSI::Util::ta_strip($string);
145              
146             # First part.
147 9         247 my ($first_wo_ansi) = $string_wo_ansi
148             =~ m/^(.{0,$self->{'line_size'}})\s+(.*)$/msx;
149 9 100       18 if (defined $first_wo_ansi) {
150 6         18 push @ret, Text::ANSI::Util::ta_trunc($string, length $first_wo_ansi);
151              
152             # Second part. (Remove first part + whitespace from string.)
153 6         1278 my $other_string_wo_ansi = Text::ANSI::Util::ta_strip(
154             Text::ANSI::Util::ta_substr($string, length $first_wo_ansi,
155             Text::ANSI::Util::ta_length($string))
156             );
157 6         1820 $other_string_wo_ansi =~ m/^(\s*)/ms;
158 6         17 my $count_of_spaces = length $1;
159 6         16 push @ret, Text::ANSI::Util::ta_substr($string, 0, (length $first_wo_ansi)
160             + $count_of_spaces, '');
161             }
162             } else {
163 12         220 @ret = $string =~ m/^(.{0,$self->{'line_size'}})\s+(.*)$/msx;
164             }
165 21         2185 return @ret;
166             }
167              
168             1;
169              
170             __END__
171              
172             =pod
173              
174             =encoding utf8
175              
176             =head1 NAME
177              
178             Indent::Word - Class for word indenting.
179              
180             =head1 SYNOPSIS
181              
182             use Indent::Word;
183              
184             my $obj = Indent::Word->new(%parameters);
185             my $string = $obj->indent('text text text');
186             my @data = $obj->indent('text text text');
187              
188             =head1 METHODS
189              
190             =head2 C<new>
191              
192             my $obj = Indent::Word->new(%parameters);
193              
194             Constructor.
195              
196             Returns instance of object.
197              
198             =over 8
199              
200             =item * C<ansi>
201              
202             Use with ANSI sequences.
203             Default value is:
204             - 1 if COLOR env variable is set
205             - 0 if NO_COLOR env variable is set
206             - 0 otherwise
207              
208             =item * C<line_size>
209              
210             Sets indent line size value.
211             Default value is 79.
212              
213             =item * C<next_indent>
214              
215             Sets output separator between indented datas for string context.
216             Default value is "\t" (tabelator).
217              
218             =item * C<output_separator>
219              
220             Output separator between data in scalar context.
221             Default value is "\n" (new line).
222              
223             =back
224              
225             =head2 C<indent>
226              
227             my $string = $obj->indent('text text text');
228              
229             or
230              
231             my @data = $obj->indent('text text text');
232              
233             Indent text by words to line_size block size.
234              
235             $act_indent - Actual indent string. Will be in each output string.
236             $non_indent - Is flag for non indenting. Default is 0.
237              
238             Returns string or array with data to print.
239              
240             =head1 ENVIRONMENT
241              
242             Output is controlled by env variables C<NO_COLOR> and C<COLOR>.
243             See L<https://no-color.org/>.
244              
245             =head1 ERRORS
246              
247             new():
248             Cannot load 'Text::ANSI::Util' module.
249             From Class::Utils::set_params():
250             Unknown parameter '%s'.
251             From Indent::Utils::line_size_check():
252             'line_size' parameter must be a positive number.
253             line_size => %s
254              
255             =head1 EXAMPLE1
256              
257             use strict;
258             use warnings;
259              
260             use Indent::Word;
261              
262             # Object.
263             my $i = Indent::Word->new(
264             'line_size' => 20,
265             );
266              
267             # Indent.
268             print $i->indent(join(' ', ('text') x 7))."\n";
269              
270             # Output:
271             # text text text text
272             # <--tab->text text text
273              
274             =head1 EXAMPLE2
275              
276             use strict;
277             use warnings;
278              
279             use Indent::Word;
280             use Term::ANSIColor;
281              
282             # Object.
283             my $i = Indent::Word->new(
284             'ansi' => 1,
285             'line_size' => 20,
286             );
287              
288             # Indent.
289             print $i->indent('text text '.color('cyan').'text'.color('reset').
290             ' text '.color('red').'text'.color('reset').' text text')."\n";
291              
292             # Output:
293             # text text text text
294             # <--tab->text text text
295              
296             =head1 DEPENDENCIES
297              
298             L<Class::Utils>,
299             L<English>,
300             L<Error::Pure>,
301             L<Indent::Utils>,
302             L<Readonly>.
303              
304             L<Text::ANSI::Util> for situation with 'ansi' => 1.
305              
306             =head1 SEE ALSO
307              
308             =over
309              
310             =item L<Indent>
311              
312             Class for indent handling.
313              
314             =item L<Indent::Block>
315              
316             Class for block indenting.
317              
318             =item L<Indent::Data>
319              
320             Class for data indenting.
321              
322             =item L<Indent::String>
323              
324             Class for text indenting.
325              
326             =item L<Indent::Utils>
327              
328             Utilities for Indent classes.
329              
330             =item L<Text::Wrap>
331              
332             line wrapping to form simple paragraphs
333              
334             =back
335              
336             =head1 REPOSITORY
337              
338             L<https://github.com/michal-josef-spacek/Indent>
339              
340             =head1 AUTHOR
341              
342             Michal Josef Špaček L<mailto:skim@cpan.org>
343              
344             L<http://skim.cz>
345              
346             =head1 LICENSE AND COPYRIGHT
347              
348             © 2005-2024 Michal Josef Špaček
349              
350             BSD 2-Clause License
351              
352             =head1 VERSION
353              
354             0.09
355              
356             =cut