File Coverage

blib/lib/YAML/Tidy.pm
Criterion Covered Total %
statement 552 598 92.3
branch 202 234 86.3
condition 122 135 90.3
subroutine 31 32 96.8
pod 4 5 80.0
total 911 1004 90.7


line stmt bran cond sub pod time code
1             # ABSTRACT: Tidy YAML files
2 6     6   455394 use strict;
  6         39  
  6         169  
3 6     6   26 use warnings;
  6         9  
  6         153  
4 6     6   26 use warnings FATAL => qw/ substr /;
  6         8  
  6         189  
5              
6 6     6   87 use v5.20;
  6         18  
7 6     6   2170 use experimental qw/ signatures /;
  6         16990  
  6         32  
8             package YAML::Tidy;
9              
10             our $VERSION = '0.006'; # VERSION
11              
12 6     6   3214 use YAML::Tidy::Node;
  6         13  
  6         161  
13 6     6   2154 use YAML::Tidy::Config;
  6         14  
  6         164  
14 6     6   2258 use YAML::LibYAML::API::XS;
  6         2681  
  6         212  
15 6         319 use YAML::PP::Common qw/
16             YAML_PLAIN_SCALAR_STYLE YAML_SINGLE_QUOTED_SCALAR_STYLE
17             YAML_DOUBLE_QUOTED_SCALAR_STYLE YAML_LITERAL_SCALAR_STYLE
18             YAML_FOLDED_SCALAR_STYLE
19             YAML_FLOW_SEQUENCE_STYLE YAML_FLOW_MAPPING_STYLE
20 6     6   32 /;
  6         7  
21 6     6   2753 use YAML::PP::Parser;
  6         126724  
  6         203  
22 6     6   2555 use YAML::PP::Highlight;
  6         208117  
  6         264  
23 6     6   45 use Data::Dumper;
  6         10  
  6         266  
24              
25 6 50   6   31 use constant DEBUG => $ENV{YAML_TIDY_DEBUG} ? 1 : 0;
  6         11  
  6         40166  
26              
27 22     22 1 1317 sub new($class, %args) {
  22         37  
  22         33  
  22         26  
28 22   66     76 my $cfg = delete $args{cfg} || YAML::Tidy::Config->new();
29             my $self = bless {
30             partial => delete $args{partial},
31 22         59 cfg => $cfg,
32             }, $class;
33 22         79 return $self;
34             }
35              
36 65578     65578 1 66281 sub cfg($self) { $self->{cfg} }
  65578         65909  
  65578         62906  
  65578         136976  
37 22505     22505 0 24532 sub partial($self) { $self->{partial} }
  22505         26246  
  22505         22142  
  22505         44953  
38              
39 3037     3037 1 4629867 sub tidy($self, $yaml) {
  3037         7111  
  3037         6922  
  3037         5227  
40 3037         6936 local $Data::Dumper::Sortkeys = 1;
41 3037         34802 my @lines = split /\n/, $yaml, -1;
42 3037         12982 my $tree = $self->_tree($yaml, \@lines);
43 3037         6299 $self->{tree} = $tree;
44 3037         11193 $self->{lines} = \@lines;
45             # warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\@lines], ['lines']);
46 3037 100       7460 if (@lines) {
47 3024         5256 my $from = 0;
48 3024 100       9223 $self->_trimspaces(\$from, $tree) if $self->cfg->trimtrailing;
49 3024         9756 $self->_process(undef, $tree);
50             }
51 3037         5114 $yaml = join "\n", @{ $self->{lines} };
  3037         13414  
52             # warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$yaml], ['yaml']);
53 3037         12454 return $yaml;
54             }
55              
56 22424     22424   24161 sub _process($self, $parent, $node) {
  22424         26139  
  22424         23165  
  22424         23334  
  22424         22260  
57 22424   100     52873 my $type = $node->{type} || '';
58             # warn __PACKAGE__.':'.__LINE__.": ======== _process($parent, $node) $type\n";
59 22424 100       38313 if ($node->{flow}) {
60 975         3177 $self->_process_flow($parent, $node);
61 975         1944 return;
62             }
63 21449         29589 my $level = $node->{level};
64 21449         30915 my $indent = $self->cfg->indent;
65 21449         30137 my $lines = $self->{lines};
66 21449 50       34863 return unless @$lines;
67              
68 21449 100 100     45205 if ($level == -1 and $type eq 'DOC') {
69 3347         9451 $self->_process_doc($parent, $node);
70             }
71 21449         46056 my $start = $node->start;
72              
73              
74 21449         25292 my $indenttoplevelscalar = 1;
75 21449         28824 my $trimtrailing = $self->cfg->trimtrailing;
76              
77 21449         37811 my $col = $node->indent;
78 21449 100       49302 my $lastcol = $parent ? $parent->indent : -99;
79 21449         29955 my $realindent = $col - $lastcol;
80 21449         36959 my $startline = $node->line;
81 21449         31897 my $line = $lines->[ $startline ];
82 21449 50       35189 unless (defined $line) {
83 0         0 die "Line $startline not found";
84             }
85 21449         45108 my $before = substr($line, 0, $col);
86              
87              
88 21449 100       36884 if ($node->is_collection) {
89 10329   100     17489 my $ignore_firstlevel = ($self->partial and $level == 0);
90 10329 100 100     26564 if ($level < 0 or $ignore_firstlevel) {
91 6372         7254 for my $c (@{ $node->{children} }) {
  6372         11777  
92 6696         12449 $self->_process($node, $c);
93             }
94 6372         14453 return;
95             }
96              
97 3957 100       7799 if ($level == 0) {
98 2274         3239 $indent = 0;
99             }
100 3957 100       9654 if ($type eq 'MAP') {
    50          
101 2492 100       6638 if ($before =~ tr/ //c) {
102 317 100       862 if ($indent == 1) {
103 12         20 $indent = 2;
104             }
105             }
106             }
107             elsif ($type eq 'SEQ') {
108 1465 100       2958 if ($before =~ tr/ //c) {
109 247 100       651 if ($indent == 1) {
110 12         19 $indent = 2;
111             }
112             }
113             else {
114 1218 100 100     4469 if ($parent->{type} eq 'MAP' and not $node->{index} % 2) {
115             # zero indented sequence?
116 370         897 $indent = $self->cfg->indent_seq_in_map;
117             }
118             }
119              
120             }
121 3957         6858 my $diff = $indent - $realindent;
122 3957 100       7561 if ($diff) {
123 588         1926 $self->_fix_indent($node, $diff, $col);
124 588         1774 $node->fix_node_indent($diff);
125             }
126 3957         4735 for my $c (@{ $node->{children} }) {
  3957         7699  
127 12704         23094 $self->_process($node, $c);
128             }
129 3957         10620 return;
130             }
131             else {
132 11120   100     18320 my $ignore_firstlevel = ($self->partial and $level == 0);
133 11120 100       22062 if ($node->empty_scalar) {
134 282         715 return;
135             }
136 10838 100       22011 if ($node->{name} eq 'alias_event') {
137 261         736 return;
138             }
139 10577 100 100     32724 if ($parent->{type} eq 'MAP' and ($node->{index} % 2 and not $node->multiline)) {
      100        
140 4395         11016 $self->_replace_quoting($node);
141 4395         11113 return;
142             }
143 6182         12423 my $new_indent = $parent->indent + $indent;
144 6182         12936 my $new_spaces = ' ' x $new_indent;
145              
146 6182         12836 my ($anchor, $tag, $comments, $scalar) = $self->_find_scalar_start($node);
147 6182         9663 my $explicit_indent = 0;
148 6182 100       17609 if ($scalar->[2] =~ m/[>|]/) {
149 901         1958 my $l = $lines->[ $scalar->[0] ];
150 901         4260 my ($ind) = substr($l, $scalar->[1]) =~ m/^[|>][+-]?([0-9]*)/;
151 901         1980 $explicit_indent = $ind;
152             }
153 6182         8631 my $skipfirst = 0;
154 6182         10907 my $before = substr($line, 0, $col);
155 6182 100       13105 if ($before =~ tr/ \t//c) {
156             # same line as key
157 5311         6144 my $remove = 0;
158 5311 50       24619 $before =~ s/([\t ]+)$/ / and $remove = -1 + length $1;
159 5311         13808 $node->open->{column} -= $remove;
160 5311 100       10552 unless ($node->multiline) {
161 4140         7385 $node->close->{column} -= $remove;
162             }
163 5311         11361 $line = $before . substr($line, $col);
164 5311         8944 $lines->[ $startline ] = $line;
165 5311         7410 $skipfirst = 1;
166             }
167 6182         9856 my $realstart = $scalar->[0];
168 6182 100       10985 unless ($ignore_firstlevel) {
169 6180         11756 for my $i ($startline .. $realstart) {
170 6331         9305 my $line = $lines->[ $i ];
171 6331 100 100     20087 if ($i == $startline and $col > 0) {
172 5591         10067 my $before = substr($line, 0, $col);
173 5591 100       10884 if ($before =~ tr/ //c) {
174 5311         9374 next;
175             }
176             }
177 1020 50       3126 unless ($line =~ tr/ //c) {
178 0         0 next;
179             }
180 1020         1517 my $remove = 0;
181 1020 50       6051 $line =~ s/^( *)/$new_spaces/ and $remove = length($1) - length($new_spaces);
182 1020 100       2955 if ($i == $startline) {
183 869         2424 $node->open->{column} -= $remove;
184 869 100       2171 unless ($node->multiline) {
185 409         908 $node->close->{column} -= $remove;
186             }
187             }
188 1020         2607 $lines->[ $i] = $line;
189             }
190             }
191             # leave alone explicitly indented block scalars
192 6182 100       10531 return if $explicit_indent;
193              
194 6027         7427 $startline = $realstart;
195 6027         11376 my $endline = $node->realendline;
196             # warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$startline], ['startline']);
197             # warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$endline], ['endline']);
198              
199 6027         8540 my $line = $lines->[ $startline ];
200 6027         7256 my $realcol = $scalar->[1];
201 6027         7658 $col = $realcol;
202              
203 6027         8067 my $nextline = $node->{nextline};
204              
205             my $block = ($node->{style} eq YAML_LITERAL_SCALAR_STYLE
206 6027   100     18308 or $node->{style} eq YAML_FOLDED_SCALAR_STYLE);
207 6027 100 100     22657 if ($block) {
    50 66        
208              
209 746         1329 $startline++;
210 746   100     4003 while ($startline < $endline and $lines->[ $startline ] !~ tr/ //c) {
211 73 100       187 if ($trimtrailing) {
212 68         141 $self->_trim($startline, $startline);
213             }
214 73         231 $startline++;
215             }
216 746 100       2427 if ($nextline > $endline + 1) {
217 68         142 $endline = $nextline - 1;
218             }
219 746         2479 my @slice = @$lines[$startline .. $endline ];
220 746         3450 my ($sp) = $lines->[ $startline ] =~ m/^( *)/;
221 746 100 100     4534 if (not $ignore_firstlevel and length($sp) != $new_indent) {
    100          
222 373         881 for my $line (@slice) {
223 1179 100       2702 unless (length $line) {
224 274         444 next;
225             }
226 905 100 100     2541 if ($line !~ tr/ //c and length($line) <= length($sp)) {
227 132 100       357 if ($trimtrailing) {
228 124         239 $line = '';
229             }
230 132         226 next;
231             }
232 773 100       1882 if ($line =~ m/^( *)\#/) {
233 42         88 my $cindent = length $1;
234 42         71 my $diff = $new_indent - length $sp;
235 42         65 $cindent += $diff;
236 42 100       96 if ($diff > 0) {
    50          
237 31         111 $line = (' ' x $diff) . $line;
238             }
239             elsif ($diff < 0) {
240 11 50       17 if ($cindent < 0) {
241 0         0 $cindent = 0;
242             }
243 11         18 $new_spaces = ' ' x $cindent;
244 11         40 $line =~ s/^ */$new_spaces/;
245             }
246             }
247             else {
248 731         5970 $line =~ s/^$sp/$new_spaces/;
249             }
250             }
251 373         2506 @$lines[$startline .. $endline ] = @slice;
252             }
253             elsif ($trimtrailing) {
254 348         759 for my $line (@slice) {
255 917 100 100     2780 if ($line !~ tr/ //c and length($line) <= length($sp)) {
256 228         387 $line = '';
257             }
258             }
259 348         1777 @$lines[$startline .. $endline ] = @slice;
260             }
261             }
262             elsif ($node->{style} == YAML_PLAIN_SCALAR_STYLE or
263             $node->{style} == YAML_SINGLE_QUOTED_SCALAR_STYLE or
264             $node->{style} == YAML_DOUBLE_QUOTED_SCALAR_STYLE) {
265 5281 50       9715 if ($node->empty_scalar) {
266 0         0 return;
267             }
268 5281         7402 my $remove = 0;
269 5281 100 100     13346 if (not $skipfirst or $node->multiline) {
270 1141         1803 my $startline = $startline;
271 1141 100       2768 $startline++ if $skipfirst;
272 1141         2390 $endline = $node->close->{line};
273 1141 50       2985 return if $startline >= @$lines;
274 1141         1871 my $line = $lines->[ $startline ];
275 1141         4264 my ($sp) = $line =~ m/^( *)/;
276 1141 100       2682 if ($ignore_firstlevel) {
277 1         3 $new_indent = length $sp;
278 1         3 $new_spaces = ' ' x $new_indent;
279             }
280 1141         3128 my @slice = @$lines[$startline .. $endline ];
281 1141 50 66     4271 if ($level == 0 and not $indenttoplevelscalar) {
282 0         0 $new_spaces = ' ' x ($new_indent - $indent);
283             }
284 1141         2147 for my $line (@slice) {
285 2048 100       4627 if ($line =~ tr/ //c) {
286 1747 50       8826 $line =~ s/^([\t ]*)/$new_spaces/
287             and $remove = length($1) - length($new_spaces);
288             }
289             }
290 1141         3260 $node->close->{column} -= $remove;
291 1141         3363 @$lines[$startline .. $endline ] = @slice;
292             }
293 5281 100       9578 if (not $node->multiline) {
294 4549         8601 $self->_replace_quoting($node);
295             }
296             }
297             }
298             }
299              
300             my $RE_INT_CORE = qr{^([+-]?(?:[0-9]+))$};
301             my $RE_FLOAT_CORE = qr{^([+-]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)(?:[eE][+-]?[0-9]+)?)$};
302             my $RE_INT_OCTAL = qr{^0o([0-7]+)$};
303             my $RE_INT_HEX = qr{^0x([0-9a-fA-F]+)$};
304             my @null = (qw/ null NULL Null ~ /, '');
305             my @true = qw/ true TRUE True /;
306             my @false = qw/ false FALSE False /;
307             my @inf = qw/ .inf .Inf .INF +.inf +.Inf +.INF -.inf -.Inf -.INF /;
308             my @nan = qw/ .nan .NaN .NAN /;
309             my @re = ($RE_INT_CORE, $RE_INT_OCTAL, $RE_INT_HEX, $RE_FLOAT_CORE);
310             my $re = join '|', @re;
311             my @all = (@null, @true, @false, @inf, @nan);
312              
313 8944     8944   10178 sub _replace_quoting($self, $node) {
  8944         9875  
  8944         11027  
  8944         9132  
314             # TODO nodes with tags or anchors
315 8944         14215 my $default_style = $self->cfg->default_scalar_style;
316             # single line flow scalars
317 8944 100 100     32232 if (defined $default_style and $node->{style} != $default_style) {
318 1276         2534 my ($changed, $new_string) = $self->_change_style($node, $default_style);
319 1276 100       2859 if ($changed) {
320 1102         1607 my $lines = $self->{lines};
321 1102         2912 my $line = $lines->[ $node->open->{line} ];
322 1102         2233 my ($from, $to) = ($node->open->{column}, $node->close->{column});
323 1102 100 100     4118 if (defined $node->{anchor} or $node->{tag}) {
324 120         283 my ($anchor, $tag, $comments, $scalar) = $self->_find_scalar_start($node);
325 120         317 $from = $scalar->[1];
326             }
327 1102         3015 substr($line, $from, $to - $from, $new_string);
328 1102         1712 my $diff = length($new_string) - ($to - $from);
329 1102 100       1813 if ($diff) {
330 1077         2188 $self->{tree}->_move_columns($node->open->{line}, $node->close->{column} + 1, $diff);
331             }
332 1102         2336 $node->close->{column} += $diff;
333 1102         2362 $lines->[ $node->open->{line} ] = $line;
334             }
335             }
336             }
337              
338 1276     1276   1515 sub _change_style($self, $node, $style) {
  1276         1434  
  1276         1455  
  1276         1618  
  1276         1400  
339 1276         1917 my $value = $node->{value};
340 1276 100 100     2216 if (grep { $_ eq $value } @all or $value =~ m/($re)/) {
  29348         45465  
341             # leave me alone
342 156         443 return (0);
343             }
344             else {
345 1120         2911 my $emit = $self->_emit_value($value, $style);
346 1120         2284 chomp $emit;
347 1120 50       2475 return (0) if $emit =~ tr/\n//;
348 1120         2008 my $first = substr($emit, 0, 1);
349 1120 100       2451 my $new_style =
    100          
350             $first eq "'" ? YAML_SINGLE_QUOTED_SCALAR_STYLE
351             : $first eq '"' ? YAML_DOUBLE_QUOTED_SCALAR_STYLE
352             : YAML_PLAIN_SCALAR_STYLE;
353 1120 100       2129 if ($new_style eq $style) {
354 1102         3306 return (1, $emit);
355             }
356             }
357 18         44 return (0);
358             }
359              
360 1120     1120   1168 sub _emit_value($self, $value, $style) {
  1120         1345  
  1120         1590  
  1120         1357  
  1120         1179  
361 1120         1615 my $options = {};
362 1120         6447 my $events = [
363             { name => 'stream_start_event' },
364             { name => 'document_start_event', implicit => 1 },
365             { name => 'scalar_event', style => $style, value => $value },
366             { name => 'document_end_event', implicit => 1 },
367             { name => 'stream_end_event' },
368             ];
369 1120         22248 return YAML::LibYAML::API::XS::emit_string_events($events, $options);
370             }
371              
372 3347     3347   4686 sub _process_doc($self, $parent, $node) {
  3347         4260  
  3347         4403  
  3347         4799  
  3347         4290  
373 3347         3693 DEBUG and say STDERR "_process_doc($node)";
374 3347         5522 my $lines = $self->{lines};
375 3347         6614 my $open = $node->open;
376 3347         6658 my $close = $node->close;
377 3347 100 100     6175 if ($node->open->{implicit} and $self->cfg->addheader and not $self->partial) {
    100 66        
      100        
      100        
      66        
378             # add ---
379 339         1106 splice @$lines, $open->{start}->{line}, 0, '---';
380 339         1287 $self->{tree}->fix_lines($open->{start}->{line}, +1);
381 339         516 $open->{start}->{line}--;
382 339         531 $open->{end}->{line}--;
383 339         634 $open->{end}->{column} = 3;
384 339         534 $open->{implicit} = 0;
385 339         422 DEBUG and say STDERR "$node";
386             }
387             elsif ($node->{index} == 1 and not $open->{implicit} and $self->cfg->removeheader and not $self->partial) {
388             # remove first ---
389 159         318 my $child = $node->{children}->[0];
390 159 100 100     1354 if ($open->{version_directive} or $open->{tag_directives} or not $child->is_collection and $child->empty_scalar) {
      100        
      100        
391             }
392             else {
393 138         388 my $startline = $open->{start}->{line};
394 138         314 my $line = $lines->[ $startline ];
395 138 100       1086 if ($line =~ m/^---[ \t]*$/) {
    50          
396 99         313 splice @$lines, $startline, 1;
397 99         636 $self->{tree}->fix_lines($open->{start}->{line}+1, -1);
398 99         186 DEBUG and say STDERR "$node";
399 99         292 $open->{implicit} = 1;
400             }
401             elsif ($line =~ s/^---[ \t]+(?=#)//) {
402 0         0 $lines->[ $startline ] = $line;
403 0         0 DEBUG and say STDERR "$node";
404 0         0 $open->{implicit} = 1;
405             }
406             }
407             }
408 3347 100 100     11598 if ($close->{implicit} and $self->cfg->addfooter and not $self->partial) {
    100 66        
      100        
      66        
409             # add ...
410 531         1731 splice @$lines, $close->{start}->{line}, 0, '...';
411 531         1742 $self->{tree}->fix_lines($close->{start}->{line}, +1);
412 531         934 $close->{end}->{column} = 3;
413 531         763 $close->{implicit} = 0;
414 531         966 DEBUG and say STDERR "$node";
415             }
416             elsif (not $close->{implicit} and $self->cfg->removefooter and not $self->partial) {
417             # remove ...
418 27         82 my $next = $parent->{children}->[ $node->{index} ];
419 27 100 66     118 if ($next and ($next->open->{version_directive} or $next->open->{tag_directives})) {
      66        
420             }
421             else {
422 12         34 my $startline = $close->{start}->{line};
423 12         25 my $line = $lines->[ $startline ];
424 12 50       82 if ($line =~ m/^\.\.\.[ \t]*$/) {
    0          
425 12         44 splice @$lines, $startline, 1;
426 12         72 $self->{tree}->fix_lines($close->{start}->{line}+1, -1);
427 12         35 $close->{implicit} = 1;
428             }
429             elsif ($line =~ s/^\.\.\.[ \t]+(?=#)//) {
430 0         0 $lines->[ $startline ] = $line;
431 0         0 $close->{implicit} = 1;
432             }
433 12         30 DEBUG and say STDERR "$node";
434             }
435             }
436             }
437              
438 24415     24415   23832 sub _trimspaces($self, $from, $node) {
  24415         24571  
  24415         23691  
  24415         24586  
  24415         23559  
439 24415 100       43761 if ($node->is_collection) {
    100          
440 11171         14840 my $level = $node->{level};
441 11171         12438 for my $c (@{ $node->{children} }) {
  11171         18540  
442 21559         33376 $self->_trimspaces($from, $c);
443             }
444 11171 100       20834 if ($level == -1) {
445 6017         13981 $self->_trim($$from, $node->end->{line});
446             }
447             }
448             elsif (defined $node->{style}) {
449             # Only spaces in block scalars must be left alone
450 12988 100 100     47329 if ($node->{style} eq YAML_LITERAL_SCALAR_STYLE
451             or $node->{style} eq YAML_FOLDED_SCALAR_STYLE) {
452 847         2356 my ($anchor, $tag, $comments, $scalar) = $self->_find_scalar_start($node);
453 847         3052 $self->_trim($$from, $scalar->[0]);
454 847         2337 $$from = $node->end->{line};
455             }
456             }
457             }
458              
459 3996     3996   4681 sub _process_flow($self, $parent, $node, $block_indent = undef) {
  3996         4509  
  3996         4594  
  3996         4398  
  3996         4678  
  3996         4297  
460 3996 50       8329 return unless $parent;
461 3996         6610 my $level = $node->{level};
462 3996   100     10803 my $flow = $node->{flow} || 0;
463 3996   66     8794 $block_indent //= $parent->indent + $self->cfg->indent;
464 3996 100       7072 $block_indent = 0 if $level == 0;
465              
466 3996 100       7069 unless ($node->is_collection) {
467 2591         5903 $self->_process_flow_scalar($parent, $node, $block_indent);
468 2591         5653 return;
469             }
470 1405 100 100     5316 if ($parent->{type} eq 'MAP' and $node->{index} % 2) {
471 97         219 return;
472             }
473 1308         2230 my $lines = $self->{lines};
474 1308         2781 my $startline = $node->start->{line};
475 1308         2704 my $end = $node->end;
476 1308         2203 my $endline = $end->{line};
477              
478 1308         3219 my $before = substr($lines->[ $startline ], 0, $node->start->{column});
479 1308 100       3577 if ($before =~ tr/ \t//c) {
480 902         1233 $startline++;
481             }
482 1308         3057 my @lines = ($startline .. $node->open->{end}->{line});
483 1308         3752 my $before_end = substr($lines->[ $endline ], 0, $end->{column} - 1);
484 1308 100       3285 unless ($before_end =~ tr/ \t//c) {
485 295         593 push @lines, $endline;
486             }
487 1308         2624 for my $i (@lines) {
488 717         1536 my $new_spaces = ' ' x $block_indent;
489 717         3980 $lines->[ $i ] =~ s/^([ \t]*)/$new_spaces/;
490 717         2011 my $old = length $1;
491 717         2223 $node->_fix_flow_indent(line => $i, diff => $block_indent - $old);
492             }
493              
494 1308         1879 for my $c (@{ $node->{children} }) {
  1308         2676  
495 3021         5927 $self->_process_flow($node, $c, $block_indent + $self->cfg->indent);
496             }
497             }
498              
499 2591     2591   3006 sub _process_flow_scalar($self, $parent, $node, $block_indent) {
  2591         2932  
  2591         3107  
  2591         2969  
  2591         2786  
  2591         2848  
500 2591 100       5058 if ($node->empty_scalar) {
501 128         227 return;
502             }
503 2463         4551 my $startline = $node->line;
504 2463         3585 my $lines = $self->{lines};
505 2463         3788 my $line = $lines->[ $startline ];
506 2463         4046 my $col = $node->start->{column};
507 2463         5646 my $before = substr($line, 0, $col);
508 2463 100       5292 if ($before =~ tr/ \t//c) {
509 2091         2427 $startline++;
510             }
511 2463         4568 my $endline = $node->end->{line};
512 2463         6048 for my $i ($startline .. $endline) {
513 476         891 my $line = $lines->[ $i ];
514 476         1053 my $new_spaces = ' ' x $block_indent;
515 476         2455 $line =~ s/^([ \t]*)/$new_spaces/;
516 476         1287 my $old = length $1;
517 476         1552 $node->_fix_flow_indent(line => $i, diff => $block_indent - $old);
518 476         1329 $lines->[ $i ] = $line;
519             }
520             }
521              
522 7149     7149   9269 sub _find_scalar_start($self, $node) {
  7149         8022  
  7149         8596  
  7149         7961  
523             # warn __PACKAGE__.':'.__LINE__.": ========= _find_scalar_start $node\n";
524 7149         9852 my $lines = $self->{lines};
525 7149         12519 my $from = $node->line;
526 7149         13752 my $to = $node->realendline;
527 7149         12697 my $col = $node->indent;
528 7149         13370 my $end = $node->end;
529 7149         10291 my $endcol = $end->{column};
530 7149         19833 my @slice = @$lines[ $from .. $to ];
531 7149         22983 my $anchor;
532             my $tag;
533 7149         0 my @comments;
534 7149         0 my $start;
535 7149         0 my $scalar;
536 7149         15371 for my $i (0 .. $#slice) {
537 7315         10418 my $line = $slice[ $i ];
538 7315 100       12437 my $f = $i == 0 ? $col : 0;
539 7315 100 33     16954 my $t = $i == $#slice ? ($endcol || length($line)) : length($line);
540 7315         17029 my $part = substr($line, $f, $t - $f);
541 7315 50       20994 if ($part =~ m/^ *(\#.*)$/g) {
542 0         0 my $comment = $1;
543 0         0 my $pos1 = length($line) - length($comment);
544 0         0 push @comments, [$i + $from, $pos1, $comment];
545 0         0 next;
546             }
547 7315         8638 my $cur;
548 7315         28829 while ($part =~ m/\G\s*([&!])(\S*)/g) {
549 1428         3641 my $type = $1;
550 1428         2389 my $name = $2;
551 1428         2320 $cur = pos $part;
552 1428         1905 my $pos = $cur - 1;
553 1428         1984 my $pos1 = $pos - length $name;
554 1428         2827 my $prop = substr($part, $pos1, 1+ length $name);
555 1428 100       4134 if ($type eq '&') {
    50          
556 509         2165 $anchor = [$i + $from, $pos1 + $f, $prop];
557             }
558             elsif ($type eq '!') {
559 919         4101 $tag = [$i + $from, $pos1 + $f, $prop];
560             }
561             }
562 7315         16138 pos($part) = $cur;
563 7315 50       18213 if ($part =~ m/\G *(\#.*)$/g) {
564 0         0 my $comment = $1;
565 0         0 $cur = pos $part;
566 0         0 my $pos1 = length($line) - length($comment);
567 0         0 push @comments, [$i + $from, $pos1, $comment];
568 0         0 next;
569             }
570 7315         11038 pos($part) = $cur;
571 7315 100       23365 if ($part =~ m/\G *(\S)/g) {
572 6973         15800 $scalar = $1;
573 6973         10028 my $pos1 = (pos $part) - 1;
574 6973         15072 $scalar = [$i + $from, $pos1 + $f, $scalar];
575 6973         13460 last;
576             }
577             }
578 7149   100     14606 $scalar ||= [$to, length($slice[ -1 ]), ''];
579             # warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$scalar], ['scalar']);
580 7149         22300 return ($anchor, $tag, \@comments, $scalar);
581             }
582              
583 6932     6932   8551 sub _trim($self, $from, $to) {
  6932         7783  
  6932         7786  
  6932         8142  
  6932         6999  
584 6932         9486 my $lines = $self->{lines};
585 6932         15724 for my $line (@$lines[ $from .. $to ]) {
586 32434         63275 $line =~ s/[\t ]+$//;
587             }
588             }
589              
590 588     588   896 sub _fix_indent($self, $node, $fix, $offset) {
  588         830  
  588         799  
  588         801  
  588         716  
  588         720  
591 588   100     1348 $offset ||= 0;
592 588         1223 my $startline = $node->line;
593 588         1138 my $lines = $self->{lines};
594 588         1194 my $endline = $node->realendline;
595 588         2097 my @slice = @$lines[$startline .. $endline];
596 588         1263 for my $line (@slice) {
597 1449 100       3357 next unless length $line;
598 1430 100       2371 if ($fix < 0) {
599 953         1218 my $offset = $offset;
600 953         1197 my $fix = -$fix;
601 953 50       1731 if ($offset > length $line) {
602 0         0 $offset = -1 + length $line;
603             }
604 953 50       1916 if ($line =~ tr/ //c) {
605 953 100       2410 if ($line =~ m/^ *\#/) {
606 18         226 $line =~ s/ {1,$fix}//;
607 18         59 next;
608             }
609             }
610             else {
611 0         0 $line =~ s/ {1,$fix}//;
612 0         0 next;
613             }
614 935         1646 my $before = substr($line, 0, $offset);
615 935         6287 $before =~ s/ {$fix,$fix}$//;
616 935         2738 $line = $before . substr($line, $offset);
617             }
618             else {
619 477 50       1096 unless ($line =~ tr/ //c) {
620 0         0 next;
621             }
622 477         1648 substr($line, $offset, 0, ' ' x $fix);
623             }
624             }
625 588         2387 @$lines[$startline .. $endline] = @slice;
626             }
627              
628 3037     3037   5340 sub _tree($self, $yaml, $lines) {
  3037         5399  
  3037         5790  
  3037         4998  
  3037         5251  
629 3037         9990 my $events = $self->_parse($yaml);
630 3037         22012 $self->{events} = $events;
631 3037         5772 my $first = shift @$events;
632 3037         5575 my $end = pop @$events;
633 3037         11761 $_->{level} = -1 for ($first, $end);
634 3037         7094 $first->{id} = -1;
635 3037         4097 _pp($first) if DEBUG;
636 3037         4667 my @stack;
637              
638 3037         4921 my $level = -1;
639 3037         29810 my $docs = YAML::Tidy::Node::Collection->new(
640             type => 'STR',
641             children => [],
642             indent => -1,
643             line => 0,
644             level => $level,
645             start => YAML::Tidy::Node::Collection->new(%$first),
646             end => YAML::Tidy::Node::Collection->new(%$end),
647             );
648 3037         6499 my $ref = $docs;
649 3037         4623 my $id = 0;
650 3037         5050 my $flow = 0;
651 3037         9161 for my $i (0 .. $#$events) {
652 31507         36081 my $event = $events->[ $i ];
653 31507         37612 my $name = $event->{name};
654 31507         31052 $id++;
655              
656 31507         31601 my $type;
657 31507 100       78766 if ($name =~ m/document_start/) {
    100          
    100          
658 3347         5941 $type = 'DOC';
659             }
660             elsif ($name =~ m/sequence_start/) {
661 2203         3180 $type = 'SEQ';
662             }
663             elsif ($name =~ m/mapping_start/) {
664 3224         5047 $type = 'MAP';
665             }
666              
667 31507         38020 $event->{id} = $id;
668 31507 100       64499 if ($name =~ m/_start_event/) {
    100          
669 8774         12952 $event->{level} = $level;
670 8774 100       18692 if ($name eq 'sequence_start_event') {
671             # inconsistency in libyaml events?
672 2203         3490 my $col = $event->{end}->{column};
673 2203 100       4717 if ($col > 0) {
674 1469         3163 my $line = $lines->[ $event->{end}->{line} ];
675 1469         4324 my $chr = substr($line, $col - 1, 1);
676 1469 100       4215 if ($chr eq '-') {
677 121         285 $event->{end}->{column}--;
678             }
679             }
680             }
681 8774 100 100     56012 if ($flow or ($event->{style} // -1) == YAML_FLOW_SEQUENCE_STYLE
      100        
      100        
      66        
682             or ($event->{style} // -1) == YAML_FLOW_MAPPING_STYLE) {
683 1469         1802 $flow++;
684             }
685 8774         23713 my $node = YAML::Tidy::Node::Collection->new(
686             children => [],
687             type => $type,
688             level => $level,
689             start => $event,
690             flow => $flow,
691             );
692 8774         11180 push @{ $ref->{children} }, $node;
  8774         16255  
693 8774         16730 $ref->{elements}++;
694 8774         13655 $node->{index} = $ref->{elements};
695 8774         11244 push @stack, $ref;
696 8774         10521 $ref = $node;
697 8774         10439 $level++;
698             }
699             elsif ($name =~ m/_end_event/) {
700 8774         10890 my $last = pop @stack;
701              
702 8774         17222 $ref->{end} = $event;
703              
704 8774         11869 $ref = $last;
705              
706 8774         9820 $level--;
707 8774         10708 $event->{level} = $level;
708 8774 100       14720 $flow-- if $flow;
709             }
710             else {
711 13959         46229 $event = YAML::Tidy::Node::Scalar->new(%$event);
712 13959         21544 $ref->{elements}++;
713 13959         19484 $event->{index} = $ref->{elements};
714 13959         22417 $event->{level} = $level;
715 13959         15850 push @{ $ref->{children} }, $event;
  13959         20512  
716             }
717 31507         38321 $event->{nextline} = -1;
718 31507 100       50757 if ($i < $#$events) {
719 28509         35817 my $next = $events->[ $i + 1 ];
720 28509         35521 my $nextline = $next->{start}->{line};
721 28509         34620 $event->{nextline} = $nextline;
722             }
723 31507         41180 _pp($event) if DEBUG;
724             }
725 3037         7174 $end->{id} = $id + 1;
726 3037         4116 _pp($end) if DEBUG;
727 3037         76406 $self->{tree} = $docs;
728 3037         11565 return $docs;
729             }
730              
731 6071     6071   5606259 sub _parse($self, $yaml) {
  6071         10977  
  6071         10862  
  6071         9524  
732 6071         10738 my @events;
733 6071         25608 YAML::LibYAML::API::XS::parse_events($yaml, \@events);
734 6071         326170 return \@events;
735             }
736              
737 0     0   0 sub _pp($event) {
  0         0  
  0         0  
738 0         0 my $name = $event->{name};
739 0         0 my $level = $event->{level};
740 0         0 $name =~ s/_event$//;
741 0         0 my $fmt = '%2d %-10s) %-14s';
742 0         0 my $indent = $level*2+2;
743 0         0 my $lstr = (' ' x $indent) . $level;
744             my @args = (
745             $event->{id}, $lstr,
746             $event->{start}->{line}, $event->{start}->{column},
747             $event->{end}->{line}, $event->{end}->{column},
748 0         0 $name,
749             );
750 0 0       0 if ($name =~ m/scalar|alias/) {
    0          
751 0         0 local $Data::Dumper::Useqq = 1;
752 0         0 my $str = Data::Dumper->Dump([$event->{value}], ['value']);
753 0         0 chomp $str;
754 0         0 $str =~ s/^\$value = //;
755 0         0 $fmt .= " %s";
756 0         0 push @args, $str;
757             }
758             elsif ($name =~ m/end/) {
759             }
760             else {
761             }
762 0         0 $fmt .= "\n";
763 0         0 printf $fmt, @args;
764             }
765              
766 6068     6068 1 32157739 sub highlight($self, $yaml, $type = 'ansi') {
  6068         12577  
  6068         12967  
  6068         10966  
  6068         8362  
767 6068         41851 my ($error, $tokens) = YAML::PP::Parser->yaml_to_tokens(string => $yaml);
768 6068 100       17507603 if ($error) {
769 244         2501 $tokens = [];
770 244         2528 my @lines = split m/(?<=\n)/, $yaml;
771 244         807 for my $line (@lines) {
772 698 50       1923 if ($line =~ s/( +\n)//) {
773 0         0 push @$tokens, { value => $line, name => 'PLAIN' };
774 0         0 push @$tokens, { value => $1, name => 'TRAILING_SPACE' };
775 0         0 next;
776             }
777 698         2070 push @$tokens, { value => $line, name => 'PLAIN' };
778             }
779             }
780 6068 50       22000 if ($type eq 'html') {
781 0         0 return YAML::PP::Highlight->htmlcolored($tokens);
782             }
783 6068         38817 return YAML::PP::Highlight->ansicolored($tokens);
784             }
785              
786             1;
787              
788             __END__