File Coverage

blib/lib/YAML/Tidy.pm
Criterion Covered Total %
statement 582 636 91.5
branch 224 258 86.8
condition 128 144 88.8
subroutine 32 34 94.1
pod 4 5 80.0
total 970 1077 90.0


line stmt bran cond sub pod time code
1             # ABSTRACT: Tidy YAML files
2 6     6   462401 use strict;
  6         33  
  6         143  
3 6     6   24 use warnings;
  6         11  
  6         140  
4 6     6   22 use warnings FATAL => qw/ substr /;
  6         9  
  6         168  
5              
6 6     6   87 use v5.20;
  6         20  
7 6     6   2242 use experimental qw/ signatures /;
  6         16830  
  6         29  
8             package YAML::Tidy;
9              
10             our $VERSION = '0.007'; # VERSION
11              
12 6     6   3093 use YAML::Tidy::Node;
  6         13  
  6         152  
13 6     6   2108 use YAML::Tidy::Config;
  6         12  
  6         158  
14 6     6   2190 use YAML::LibYAML::API::XS;
  6         2506  
  6         214  
15 6         257 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   34 /;
  6         9  
21 6     6   2833 use YAML::PP::Parser;
  6         127567  
  6         191  
22 6     6   2366 use YAML::PP::Highlight;
  6         209953  
  6         243  
23 6     6   38 use Data::Dumper;
  6         13  
  6         255  
24              
25 6 50   6   29 use constant DEBUG => $ENV{YAML_TIDY_DEBUG} ? 1 : 0;
  6         12  
  6         43988  
26              
27 22     22 1 1122 sub new($class, %args) {
  22         37  
  22         34  
  22         29  
28 22   66     76 my $cfg = delete $args{cfg} || YAML::Tidy::Config->new();
29             my $self = bless {
30             partial => delete $args{partial},
31 22         64 cfg => $cfg,
32             }, $class;
33 22         83 return $self;
34             }
35              
36 67802     67802 1 67839 sub cfg($self) { $self->{cfg} }
  67802         67966  
  67802         64307  
  67802         139756  
37 22505     22505 0 24046 sub partial($self) { $self->{partial} }
  22505         24762  
  22505         24130  
  22505         43289  
38              
39 3037     3037 1 4488170 sub tidy($self, $yaml) {
  3037         5686  
  3037         5827  
  3037         4221  
40 3037         6008 local $Data::Dumper::Sortkeys = 1;
41 3037         32455 my @lines = split /\n/, $yaml, -1;
42 3037         11089 my $tree = $self->_tree($yaml, \@lines);
43 3037         5379 $self->{tree} = $tree;
44 3037         9224 $self->{lines} = \@lines;
45             # warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\@lines], ['lines']);
46 3037 100       7363 if (@lines) {
47 3024         4216 my $from = 0;
48 3024 100       7035 $self->_trimspaces(\$from, $tree) if $self->cfg->trimtrailing;
49 3024         8753 $self->_process(undef, $tree);
50             }
51 3037         4191 $yaml = join "\n", @{ $self->{lines} };
  3037         11640  
52             # warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$yaml], ['yaml']);
53 3037         10630 return $yaml;
54             }
55              
56 22424     22424   24007 sub _process($self, $parent, $node) {
  22424         24425  
  22424         23003  
  22424         24122  
  22424         21637  
57 22424   100     49512 my $type = $node->{type} || '';
58             # warn __PACKAGE__.':'.__LINE__.": ======== _process($parent, $node) $type\n";
59 22424 100       38183 if ($node->{flow}) {
60 975         2905 $self->_process_flow($parent, $node);
61 975         1931 return;
62             }
63 21449         25980 my $level = $node->{level};
64 21449         30898 my $indent = $self->cfg->indent;
65 21449         29259 my $lines = $self->{lines};
66 21449 50       34168 return unless @$lines;
67              
68 21449 100 100     43477 if ($level == -1 and $type eq 'DOC') {
69 3347         8256 $self->_process_doc($parent, $node);
70             }
71 21449         40022 my $start = $node->start;
72              
73              
74 21449         24551 my $indenttoplevelscalar = 1;
75 21449         29253 my $trimtrailing = $self->cfg->trimtrailing;
76              
77 21449         38887 my $col = $node->indent;
78 21449 100       47840 my $lastcol = $parent ? $parent->indent : -99;
79 21449         29375 my $realindent = $col - $lastcol;
80 21449         36221 my $startline = $node->line;
81 21449         32936 my $line = $lines->[ $startline ];
82 21449 50       35872 unless (defined $line) {
83 0         0 die "Line $startline not found";
84             }
85 21449         45908 my $before = substr($line, 0, $col);
86              
87              
88 21449 100       39781 if ($node->is_collection) {
89 10329   100     16630 my $ignore_firstlevel = ($self->partial and $level == 0);
90 10329 100 100     25776 if ($level < 0 or $ignore_firstlevel) {
91 6372         7304 for my $c (@{ $node->{children} }) {
  6372         11776  
92 6696         13252 $self->_process($node, $c);
93             }
94 6372         13961 return;
95             }
96              
97 3957 100       7499 if ($level == 0) {
98 2274         3138 $indent = 0;
99             }
100 3957 100       8503 if ($type eq 'MAP') {
    50          
101 2492 100       5708 if ($before =~ tr/ //c) {
102 317 100       879 if ($indent == 1) {
103 12         19 $indent = 2;
104             }
105             }
106             }
107             elsif ($type eq 'SEQ') {
108 1465 100       3076 if ($before =~ tr/ //c) {
109 247 100       567 if ($indent == 1) {
110 12         17 $indent = 2;
111             }
112             }
113             else {
114 1218 100 100     4102 if ($parent->{type} eq 'MAP' and not $node->{index} % 2) {
115             # zero indented sequence?
116 370         856 $indent = $self->cfg->indent_seq_in_map;
117             }
118             }
119              
120             }
121 3957         5634 my $diff = $indent - $realindent;
122 3957 100       6716 if ($diff) {
123 588         1733 $self->_fix_indent($node, $diff, $col);
124 588         1734 $node->fix_node_indent($diff);
125             }
126 3957         4448 for my $c (@{ $node->{children} }) {
  3957         6886  
127 12704         22526 $self->_process($node, $c);
128             }
129 3957         9448 return;
130             }
131             else {
132 11120   100     17893 my $ignore_firstlevel = ($self->partial and $level == 0);
133 11120 100       21019 if ($node->empty_scalar) {
134 282         715 return;
135             }
136 10838 100       20880 if ($node->{name} eq 'alias_event') {
137 261         696 return;
138             }
139 10577 100 100     31136 if ($parent->{type} eq 'MAP' and ($node->{index} % 2 and not $node->multiline)) {
      100        
140 4395         10378 $self->_replace_quoting($node);
141 4395         10931 return;
142             }
143 6182         12285 my $new_indent = $parent->indent + $indent;
144 6182         11932 my $new_spaces = ' ' x $new_indent;
145              
146 6182         13158 my ($anchor, $tag, $comments, $scalar) = $self->_find_scalar_start($node);
147 6182         9263 my $explicit_indent = 0;
148 6182 100       16483 if ($scalar->[2] =~ m/[>|]/) {
149 901         1811 my $l = $lines->[ $scalar->[0] ];
150 901         3861 my ($ind) = substr($l, $scalar->[1]) =~ m/^[|>][+-]?([0-9]*)/;
151 901         1731 $explicit_indent = $ind;
152             }
153 6182         7768 my $skipfirst = 0;
154 6182         11042 my $before = substr($line, 0, $col);
155 6182 100       12803 if ($before =~ tr/ \t//c) {
156             # same line as key
157 5311         5994 my $remove = 0;
158 5311 50       23413 $before =~ s/([\t ]+)$/ / and $remove = -1 + length $1;
159 5311         13152 $node->open->{column} -= $remove;
160 5311 100       9935 unless ($node->multiline) {
161 4140         7389 $node->close->{column} -= $remove;
162             }
163 5311         11032 $line = $before . substr($line, $col);
164 5311         9294 $lines->[ $startline ] = $line;
165 5311         6498 $skipfirst = 1;
166             }
167 6182         8954 my $realstart = $scalar->[0];
168 6182 100       10003 unless ($ignore_firstlevel) {
169 6180         10644 for my $i ($startline .. $realstart) {
170 6331         8898 my $line = $lines->[ $i ];
171 6331 100 100     19230 if ($i == $startline and $col > 0) {
172 5591         8687 my $before = substr($line, 0, $col);
173 5591 100       10257 if ($before =~ tr/ //c) {
174 5311         9839 next;
175             }
176             }
177 1020 50       2673 unless ($line =~ tr/ //c) {
178 0         0 next;
179             }
180 1020         1426 my $remove = 0;
181 1020 50       5310 $line =~ s/^( *)/$new_spaces/ and $remove = length($1) - length($new_spaces);
182 1020 100       2765 if ($i == $startline) {
183 869         2225 $node->open->{column} -= $remove;
184 869 100       1918 unless ($node->multiline) {
185 409         989 $node->close->{column} -= $remove;
186             }
187             }
188 1020         2325 $lines->[ $i] = $line;
189             }
190             }
191             # leave alone explicitly indented block scalars
192 6182 100       10387 return if $explicit_indent;
193              
194 6027         7221 $startline = $realstart;
195 6027         11126 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         8891 my $line = $lines->[ $startline ];
200 6027         7479 my $realcol = $scalar->[1];
201 6027         6846 $col = $realcol;
202              
203 6027         8104 my $nextline = $node->{nextline};
204              
205             my $block = ($node->{style} eq YAML_LITERAL_SCALAR_STYLE
206 6027   100     17552 or $node->{style} eq YAML_FOLDED_SCALAR_STYLE);
207 6027 100 100     20221 if ($block) {
    50 66        
208              
209 746         1204 $startline++;
210 746   100     3412 while ($startline < $endline and $lines->[ $startline ] !~ tr/ //c) {
211 73 100       191 if ($trimtrailing) {
212 68         140 $self->_trim($startline, $startline);
213             }
214 73         246 $startline++;
215             }
216 746 100       1839 if ($nextline > $endline + 1) {
217 68         113 $endline = $nextline - 1;
218             }
219 746         2182 my @slice = @$lines[$startline .. $endline ];
220 746         3071 my ($sp) = $lines->[ $startline ] =~ m/^( *)/;
221 746 100 100     3826 if (not $ignore_firstlevel and length($sp) != $new_indent) {
    100          
222 373         766 for my $line (@slice) {
223 1179 100       2533 unless (length $line) {
224 274         425 next;
225             }
226 905 100 100     2317 if ($line !~ tr/ //c and length($line) <= length($sp)) {
227 132 100       300 if ($trimtrailing) {
228 124         187 $line = '';
229             }
230 132         196 next;
231             }
232 773 100       1705 if ($line =~ m/^( *)\#/) {
233 42         82 my $cindent = length $1;
234 42         64 my $diff = $new_indent - length $sp;
235 42         57 $cindent += $diff;
236 42 100       92 if ($diff > 0) {
    50          
237 31         97 $line = (' ' x $diff) . $line;
238             }
239             elsif ($diff < 0) {
240 11 50       16 if ($cindent < 0) {
241 0         0 $cindent = 0;
242             }
243 11         17 $new_spaces = ' ' x $cindent;
244 11         36 $line =~ s/^ */$new_spaces/;
245             }
246             }
247             else {
248 731         5105 $line =~ s/^$sp/$new_spaces/;
249             }
250             }
251 373         2363 @$lines[$startline .. $endline ] = @slice;
252             }
253             elsif ($trimtrailing) {
254 348         669 for my $line (@slice) {
255 917 100 100     2545 if ($line !~ tr/ //c and length($line) <= length($sp)) {
256 228         392 $line = '';
257             }
258             }
259 348         1643 @$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       9738 if ($node->empty_scalar) {
266 0         0 return;
267             }
268 5281         6881 my $remove = 0;
269 5281 100 100     12015 if (not $skipfirst or $node->multiline) {
270 1141         1769 my $startline = $startline;
271 1141 100       2225 $startline++ if $skipfirst;
272 1141         2189 $endline = $node->close->{line};
273 1141 50       2841 return if $startline >= @$lines;
274 1141         1827 my $line = $lines->[ $startline ];
275 1141         4267 my ($sp) = $line =~ m/^( *)/;
276 1141 100       2381 if ($ignore_firstlevel) {
277 1         2 $new_indent = length $sp;
278 1         3 $new_spaces = ' ' x $new_indent;
279             }
280 1141         2964 my @slice = @$lines[$startline .. $endline ];
281 1141 50 66     4007 if ($level == 0 and not $indenttoplevelscalar) {
282 0         0 $new_spaces = ' ' x ($new_indent - $indent);
283             }
284 1141         1918 for my $line (@slice) {
285 2048 100       4249 if ($line =~ tr/ //c) {
286 1747 50       9113 $line =~ s/^([\t ]*)/$new_spaces/
287             and $remove = length($1) - length($new_spaces);
288             }
289             }
290 1141         2659 $node->close->{column} -= $remove;
291 1141         3075 @$lines[$startline .. $endline ] = @slice;
292             }
293 5281 100       9770 if (not $node->multiline) {
294 4549         8536 $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 11294     11294   12541 sub _replace_quoting($self, $node) {
  11294         11882  
  11294         11830  
  11294         11134  
314 11294 100       21925 return if $node->{tag};
315 10419         16921 my $default_style = $self->cfg->default_scalar_style;
316             # single line flow scalars
317 10419 100 100     33061 if (defined $default_style and $node->{style} != $default_style) {
318 1508         3480 my ($changed, $new_string, $new_style) = $self->_change_style($node, $default_style);
319 1508 100       3478 if ($changed) {
320 1341         1969 my $lines = $self->{lines};
321 1341         3608 my $line = $lines->[ $node->open->{line} ];
322 1341         2605 my ($from, $to) = ($node->open->{column}, $node->close->{column});
323 1341 100 66     5331 if (defined $node->{anchor} or $node->{tag}) {
324 58         166 my ($anchor, $tag, $comments, $scalar) = $self->_find_scalar_start($node);
325 58         158 $from = $scalar->[1];
326             }
327 1341         3606 substr($line, $from, $to - $from, $new_string);
328 1341         2185 my $diff = length($new_string) - ($to - $from);
329 1341 100       2103 if ($diff) {
330 1314         2693 $self->{tree}->_move_columns($node->open->{line}, $node->close->{column} + 1, $diff);
331             }
332 1341         2481 $node->{style} = $new_style;
333 1341         2228 $node->close->{column} += $diff;
334 1341         2401 $lines->[ $node->open->{line} ] = $line;
335             }
336             }
337             }
338              
339 1508     1508   1792 sub _change_style($self, $node, $style) {
  1508         1703  
  1508         1668  
  1508         1902  
  1508         1721  
340 1508         2153 my $value = $node->{value};
341 1508 100 100     2784 if (grep { $_ eq $value } @all or $value =~ m/($re)/) {
  34684         52350  
342             # leave me alone
343 150 100 100     627 if ($node->{style} eq YAML_PLAIN_SCALAR_STYLE or $style eq YAML_PLAIN_SCALAR_STYLE) {
344 149         438 return (0);
345             }
346             }
347              
348 1359         3036 my $emit = $self->_emit_value($value, $style);
349 1359         2708 chomp $emit;
350 1359 50       2846 return (0) if $emit =~ tr/\n//;
351 1359         2415 my $first = substr($emit, 0, 1);
352 1359 100       3381 my $new_style =
    100          
353             $first eq "'" ? YAML_SINGLE_QUOTED_SCALAR_STYLE
354             : $first eq '"' ? YAML_DOUBLE_QUOTED_SCALAR_STYLE
355             : YAML_PLAIN_SCALAR_STYLE;
356 1359 100       2567 if ($new_style eq $style) {
357 1341         4520 return (1, $emit, $new_style);
358             }
359              
360 18         51 return (0);
361             }
362              
363 1359     1359   1865 sub _emit_value($self, $value, $style) {
  1359         1572  
  1359         1970  
  1359         1512  
  1359         1369  
364 1359         2178 my $options = {};
365 1359         7665 my $events = [
366             { name => 'stream_start_event' },
367             { name => 'document_start_event', implicit => 1 },
368             { name => 'scalar_event', style => $style, value => $value },
369             { name => 'document_end_event', implicit => 1 },
370             { name => 'stream_end_event' },
371             ];
372 1359         25936 return YAML::LibYAML::API::XS::emit_string_events($events, $options);
373             }
374              
375 3347     3347   4050 sub _process_doc($self, $parent, $node) {
  3347         3931  
  3347         3750  
  3347         3690  
  3347         3708  
376 3347         4058 DEBUG and say STDERR "_process_doc($node)";
377 3347         4897 my $lines = $self->{lines};
378 3347         6177 my $open = $node->open;
379 3347         7035 my $close = $node->close;
380 3347 100 100     6102 if ($node->open->{implicit} and $self->cfg->addheader and not $self->partial) {
    100 66        
      100        
      100        
      66        
381             # add ---
382 339         1023 splice @$lines, $open->{start}->{line}, 0, '---';
383 339         1220 $self->{tree}->fix_lines($open->{start}->{line}, +1);
384 339         636 $open->{start}->{line}--;
385 339         585 $open->{end}->{line}--;
386 339         637 $open->{end}->{column} = 3;
387 339         491 $open->{implicit} = 0;
388 339         430 DEBUG and say STDERR "$node";
389             }
390             elsif ($node->{index} == 1 and not $open->{implicit} and $self->cfg->removeheader and not $self->partial) {
391             # remove first ---
392 159         277 my $child = $node->{children}->[0];
393 159 100 100     898 if ($open->{version_directive} or $open->{tag_directives} or not $child->is_collection and $child->empty_scalar) {
      100        
      100        
394             }
395             else {
396 138         271 my $startline = $open->{start}->{line};
397 138         281 my $line = $lines->[ $startline ];
398 138 100       794 if ($line =~ m/^---[ \t]*$/) {
    50          
399 99         201 splice @$lines, $startline, 1;
400 99         406 $self->{tree}->fix_lines($open->{start}->{line}+1, -1);
401 99         145 DEBUG and say STDERR "$node";
402 99         220 $open->{implicit} = 1;
403             }
404             elsif ($line =~ s/^---[ \t]+(?=#)//) {
405 0         0 $lines->[ $startline ] = $line;
406 0         0 DEBUG and say STDERR "$node";
407 0         0 $open->{implicit} = 1;
408             }
409             }
410             }
411 3347 100 100     9953 if ($close->{implicit} and $self->cfg->addfooter and not $self->partial) {
    100 66        
      100        
      66        
412             # add ...
413 531         1587 splice @$lines, $close->{start}->{line}, 0, '...';
414 531         1754 $self->{tree}->fix_lines($close->{start}->{line}, +1);
415 531         927 $close->{end}->{column} = 3;
416 531         748 $close->{implicit} = 0;
417 531         813 DEBUG and say STDERR "$node";
418             }
419             elsif (not $close->{implicit} and $self->cfg->removefooter and not $self->partial) {
420             # remove ...
421 27         73 my $next = $parent->{children}->[ $node->{index} ];
422 27 100 66     99 if ($next and ($next->open->{version_directive} or $next->open->{tag_directives})) {
      66        
423             }
424             else {
425 12         27 my $startline = $close->{start}->{line};
426 12         29 my $line = $lines->[ $startline ];
427 12 50       65 if ($line =~ m/^\.\.\.[ \t]*$/) {
    0          
428 12         34 splice @$lines, $startline, 1;
429 12         58 $self->{tree}->fix_lines($close->{start}->{line}+1, -1);
430 12         28 $close->{implicit} = 1;
431             }
432             elsif ($line =~ s/^\.\.\.[ \t]+(?=#)//) {
433 0         0 $lines->[ $startline ] = $line;
434 0         0 $close->{implicit} = 1;
435             }
436 12         24 DEBUG and say STDERR "$node";
437             }
438             }
439             }
440              
441 24415     24415   23776 sub _trimspaces($self, $from, $node) {
  24415         25588  
  24415         24212  
  24415         23508  
  24415         22749  
442 24415 100       40913 if ($node->is_collection) {
    100          
443 11171         14611 my $level = $node->{level};
444 11171         12121 for my $c (@{ $node->{children} }) {
  11171         16258  
445 21559         33862 $self->_trimspaces($from, $c);
446             }
447 11171 100       20803 if ($level == -1) {
448 6017         13773 $self->_trim($$from, $node->end->{line});
449             }
450             }
451             elsif (defined $node->{style}) {
452             # Only spaces in block scalars must be left alone
453 12988 100 100     47115 if ($node->{style} eq YAML_LITERAL_SCALAR_STYLE
454             or $node->{style} eq YAML_FOLDED_SCALAR_STYLE) {
455 847         2159 my ($anchor, $tag, $comments, $scalar) = $self->_find_scalar_start($node);
456 847         2988 $self->_trim($$from, $scalar->[0]);
457 847         2204 $$from = $node->end->{line};
458             }
459             }
460             }
461              
462 3996     3996   4679 sub _process_flow($self, $parent, $node, $block_indent = undef) {
  3996         4633  
  3996         4378  
  3996         4328  
  3996         4881  
  3996         4478  
463 3996 50       8411 return unless $parent;
464 3996         6897 my $level = $node->{level};
465 3996   50     7732 my $flow = $node->{flow} || 0;
466 3996   66     7905 $block_indent //= $parent->indent + $self->cfg->indent;
467 3996 100       7238 $block_indent = 0 if $level == 0;
468              
469 3996 100       7588 unless ($node->is_collection) {
470 2591         6049 $self->_process_flow_scalar($parent, $node, $block_indent);
471 2591         6420 return;
472             }
473 1405 100 100     4625 if ($parent->{type} eq 'MAP' and $node->{index} % 2) {
474 97         221 return;
475             }
476 1308         2054 my $lines = $self->{lines};
477 1308         2714 my $startline = $node->start->{line};
478 1308         2678 my $end = $node->end;
479 1308         2162 my $endline = $end->{line};
480              
481 1308         2647 my $before = substr($lines->[ $startline ], 0, $node->start->{column});
482 1308 100       3461 if ($before =~ tr/ \t//c) {
483 919         1254 $startline++;
484             }
485 1308         2842 my @lines = ($startline .. $node->open->{end}->{line});
486 1308         3590 my $before_end = substr($lines->[ $endline ], 0, $end->{column} - 1);
487 1308 100       2995 unless ($before_end =~ tr/ \t//c) {
488 294         599 push @lines, $endline;
489             }
490 1308         2448 for my $i (@lines) {
491 699         1370 my $new_spaces = ' ' x $block_indent;
492 699         3604 $lines->[ $i ] =~ s/^([ \t]*)/$new_spaces/;
493 699         1706 my $old = length $1;
494 699         2049 $node->_fix_flow_indent(line => $i, diff => $block_indent - $old);
495             }
496              
497 1308         1638 for my $c (@{ $node->{children} }) {
  1308         2430  
498 3021         5243 $self->_process_flow($node, $c, $block_indent + $self->cfg->indent);
499             }
500             }
501              
502 2591     2591   2965 sub _process_flow_scalar($self, $parent, $node, $block_indent) {
  2591         2867  
  2591         2688  
  2591         2770  
  2591         2863  
  2591         2743  
503 2591 100       4568 if ($node->empty_scalar) {
504 128         224 return;
505             }
506 2463         4740 my $startline = $node->line;
507 2463         3595 my $lines = $self->{lines};
508 2463         3994 my $line = $lines->[ $startline ];
509 2463         4120 my $col = $node->start->{column};
510 2463         5926 my $before = substr($line, 0, $col);
511 2463 100       5485 if ($before =~ tr/ \t//c) {
512 2101         2513 $startline++;
513             }
514 2463         4718 my $endline = $node->end->{line};
515 2463         5226 for my $i ($startline .. $endline) {
516 466         920 my $line = $lines->[ $i ];
517 466         912 my $new_spaces = ' ' x $block_indent;
518 466         2281 $line =~ s/^([ \t]*)/$new_spaces/;
519 466         1172 my $old = length $1;
520 466 100       1168 if ($block_indent != $old) {
521 415         1289 $self->{tree}->_fix_flow_indent(line => $i, diff => $block_indent - $old);
522             }
523 466         1289 $lines->[ $i ] = $line;
524             }
525 2463 100       4399 if (not $node->multiline) {
526 2359         5494 $self->_check_adjacency($node, $parent);
527 2359 100       5794 if ($node->{name} eq 'scalar_event') {
528 2350         4335 $self->_replace_quoting($node);
529             }
530             }
531             }
532              
533 2359     2359   2724 sub _check_adjacency($self, $node, $parent) {
  2359         2601  
  2359         2588  
  2359         2549  
  2359         2368  
534 2359 50       4217 return unless $node->{flow};
535 2359 100       4976 return unless $node->is_mapping_value($parent);
536             # allowed: "foo":bar, "foo":*alias
537             # not allowed: foo:bar, foo:*alias
538 749         2263 my $prev = $parent->sibling($node, -1);
539 749         1639 my $tidy_adjacency = $self->cfg->adjacency;
540 749 100 100     1598 if (not $prev->is_collection and not $prev->is_quoted) {
541 527         798 $tidy_adjacency = 0; # adjacency would be invalid here
542             }
543 749         1597 my $start = $node->open;
544 749         1033 my $adjacent = 0;
545 749         1660 my $line = $self->{lines}->[ $start->{line} ];
546 749 100 66     3619 if ($start->{column} > 0 and substr($line, $start->{column} - 1, 1) eq ':') {
547 56         100 $adjacent = 1;
548             }
549 749 100       1673 return unless defined $tidy_adjacency; # keep as is
550 527 50       1046 if ($tidy_adjacency) {
551 0         0 die "Not implemented yet: enforce adjacency";
552             }
553 527 100       1295 return unless $adjacent;
554 2         7 substr($line, $start->{column}, 0, ' ');
555 2         9 $self->{tree}->_move_columns($start->{line}, $start->{column} + 1, +1);
556 2         8 $self->{lines}->[ $start->{line} ] = $line;
557             }
558              
559 7087     7087   8240 sub _find_scalar_start($self, $node) {
  7087         8047  
  7087         7759  
  7087         7477  
560             # warn __PACKAGE__.':'.__LINE__.": ========= _find_scalar_start $node\n";
561 7087         9451 my $lines = $self->{lines};
562 7087         12251 my $from = $node->line;
563 7087         12617 my $to = $node->realendline;
564 7087         13177 my $col = $node->indent;
565 7087         12383 my $end = $node->end;
566 7087         12646 my $endcol = $end->{column};
567 7087         19471 my @slice = @$lines[ $from .. $to ];
568 7087         21701 my $anchor;
569             my $tag;
570 7087         0 my @comments;
571 7087         0 my $start;
572 7087         0 my $scalar;
573 7087         14606 for my $i (0 .. $#slice) {
574 7253         10588 my $line = $slice[ $i ];
575 7253 100       11755 my $f = $i == 0 ? $col : 0;
576 7253 100 33     16374 my $t = $i == $#slice ? ($endcol || length($line)) : length($line);
577 7253         17279 my $part = substr($line, $f, $t - $f);
578 7253 50       19258 if ($part =~ m/^ *(\#.*)$/g) {
579 0         0 my $comment = $1;
580 0         0 my $pos1 = length($line) - length($comment);
581 0         0 push @comments, [$i + $from, $pos1, $comment];
582 0         0 next;
583             }
584 7253         8779 my $cur;
585 7253         25784 while ($part =~ m/\G\s*([&!])(\S*)/g) {
586 1354         3117 my $type = $1;
587 1354         2178 my $name = $2;
588 1354         1953 $cur = pos $part;
589 1354         1761 my $pos = $cur - 1;
590 1354         1694 my $pos1 = $pos - length $name;
591 1354         2455 my $prop = substr($part, $pos1, 1+ length $name);
592 1354 100       3312 if ($type eq '&') {
    50          
593 501         1974 $anchor = [$i + $from, $pos1 + $f, $prop];
594             }
595             elsif ($type eq '!') {
596 853         3553 $tag = [$i + $from, $pos1 + $f, $prop];
597             }
598             }
599 7253         14778 pos($part) = $cur;
600 7253 50       17647 if ($part =~ m/\G *(\#.*)$/g) {
601 0         0 my $comment = $1;
602 0         0 $cur = pos $part;
603 0         0 my $pos1 = length($line) - length($comment);
604 0         0 push @comments, [$i + $from, $pos1, $comment];
605 0         0 next;
606             }
607 7253         10644 pos($part) = $cur;
608 7253 100       21770 if ($part =~ m/\G *(\S)/g) {
609 6911         14386 $scalar = $1;
610 6911         9804 my $pos1 = (pos $part) - 1;
611 6911         14024 $scalar = [$i + $from, $pos1 + $f, $scalar];
612 6911         13190 last;
613             }
614             }
615 7087   100     13870 $scalar ||= [$to, length($slice[ -1 ]), ''];
616             # warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$scalar], ['scalar']);
617 7087         22194 return ($anchor, $tag, \@comments, $scalar);
618             }
619              
620 6932     6932   9140 sub _trim($self, $from, $to) {
  6932         7563  
  6932         7548  
  6932         7030  
  6932         6945  
621 6932         8464 my $lines = $self->{lines};
622 6932         14054 for my $line (@$lines[ $from .. $to ]) {
623 32434         61611 $line =~ s/[\t ]+$//;
624             }
625             }
626              
627 588     588   836 sub _fix_indent($self, $node, $fix, $offset) {
  588         747  
  588         701  
  588         799  
  588         702  
  588         668  
628 588   100     1207 $offset ||= 0;
629 588         1148 my $startline = $node->line;
630 588         1131 my $lines = $self->{lines};
631 588         1251 my $endline = $node->realendline;
632 588         1880 my @slice = @$lines[$startline .. $endline];
633 588         1131 for my $line (@slice) {
634 1449 100       3250 next unless length $line;
635 1430 100       2541 if ($fix < 0) {
636 953         1207 my $offset = $offset;
637 953         1160 my $fix = -$fix;
638 953 50       1681 if ($offset > length $line) {
639 0         0 $offset = -1 + length $line;
640             }
641 953 50       1772 if ($line =~ tr/ //c) {
642 953 100       2289 if ($line =~ m/^ *\#/) {
643 18         231 $line =~ s/ {1,$fix}//;
644 18         66 next;
645             }
646             }
647             else {
648 0         0 $line =~ s/ {1,$fix}//;
649 0         0 next;
650             }
651 935         1591 my $before = substr($line, 0, $offset);
652 935         5637 $before =~ s/ {$fix,$fix}$//;
653 935         2666 $line = $before . substr($line, $offset);
654             }
655             else {
656 477 50       1050 unless ($line =~ tr/ //c) {
657 0         0 next;
658             }
659 477         1465 substr($line, $offset, 0, ' ' x $fix);
660             }
661             }
662 588         2291 @$lines[$startline .. $endline] = @slice;
663             }
664              
665 3037     3037   5270 sub _tree($self, $yaml, $lines) {
  3037         4836  
  3037         4723  
  3037         4034  
  3037         3749  
666 3037         7781 my $events = $self->_parse($yaml);
667 3037         18461 $self->{events} = $events;
668 3037         5640 my $first = shift @$events;
669 3037         4630 my $end = pop @$events;
670 3037         9572 $_->{level} = -1 for ($first, $end);
671 3037         5434 $first->{id} = -1;
672 3037         3564 _pp($first) if DEBUG;
673 3037         4697 my @stack;
674              
675 3037         4160 my $level = -1;
676 3037         25595 my $docs = YAML::Tidy::Node::Collection->new(
677             type => 'STR',
678             children => [],
679             indent => -1,
680             line => 0,
681             level => $level,
682             start => YAML::Tidy::Node::Collection->new(%$first),
683             end => YAML::Tidy::Node::Collection->new(%$end),
684             );
685 3037         6141 my $ref = $docs;
686 3037         4481 my $id = 0;
687 3037         4246 my $flow = 0;
688 3037         8413 for my $i (0 .. $#$events) {
689 31507         36776 my $event = $events->[ $i ];
690 31507         36696 my $name = $event->{name};
691 31507         30845 $id++;
692              
693 31507         30819 my $type;
694 31507 100       77939 if ($name =~ m/document_start/) {
    100          
    100          
695 3347         5192 $type = 'DOC';
696             }
697             elsif ($name =~ m/sequence_start/) {
698 2203         2995 $type = 'SEQ';
699             }
700             elsif ($name =~ m/mapping_start/) {
701 3224         4219 $type = 'MAP';
702             }
703              
704 31507         39229 $event->{id} = $id;
705 31507 100       60418 if ($name =~ m/_start_event/) {
    100          
706 8774         12500 $event->{level} = $level;
707 8774 100       17126 if ($name eq 'sequence_start_event') {
708             # inconsistency in libyaml events?
709 2203         3296 my $col = $event->{end}->{column};
710 2203 100       4600 if ($col > 0) {
711 1469         2924 my $line = $lines->[ $event->{end}->{line} ];
712 1469         3961 my $chr = substr($line, $col - 1, 1);
713 1469 100       3821 if ($chr eq '-') {
714 121         269 $event->{end}->{column}--;
715             }
716             }
717             }
718 8774 100 100     48284 if ($flow or ($event->{style} // -1) == YAML_FLOW_SEQUENCE_STYLE
      100        
      100        
      66        
719             or ($event->{style} // -1) == YAML_FLOW_MAPPING_STYLE) {
720 1469         1949 $flow++;
721             }
722 8774         21849 my $node = YAML::Tidy::Node::Collection->new(
723             children => [],
724             type => $type,
725             level => $level,
726             start => $event,
727             flow => $flow,
728             );
729 8774         11184 push @{ $ref->{children} }, $node;
  8774         15975  
730 8774         16087 $ref->{elements}++;
731 8774         12762 $node->{index} = $ref->{elements};
732 8774         10494 push @stack, $ref;
733 8774         9852 $ref = $node;
734 8774         10289 $level++;
735             }
736             elsif ($name =~ m/_end_event/) {
737 8774         11598 my $last = pop @stack;
738              
739 8774         16789 $ref->{end} = $event;
740              
741 8774         10183 $ref = $last;
742              
743 8774         8890 $level--;
744 8774         10285 $event->{level} = $level;
745 8774 100       15034 $flow-- if $flow;
746             }
747             else {
748 13959         46854 $event = YAML::Tidy::Node::Scalar->new(%$event, flow => $flow);
749 13959         21656 $ref->{elements}++;
750 13959         23233 $event->{index} = $ref->{elements};
751 13959         17040 $event->{level} = $level;
752 13959         14610 push @{ $ref->{children} }, $event;
  13959         20085  
753             }
754 31507         37170 $event->{nextline} = -1;
755 31507 100       50231 if ($i < $#$events) {
756 28509         35525 my $next = $events->[ $i + 1 ];
757 28509         34288 my $nextline = $next->{start}->{line};
758 28509         33702 $event->{nextline} = $nextline;
759             }
760 31507         39674 _pp($event) if DEBUG;
761             }
762 3037         5702 $end->{id} = $id + 1;
763 3037         3705 _pp($end) if DEBUG;
764 3037         61500 $self->{tree} = $docs;
765 3037         10607 return $docs;
766             }
767              
768 6071     6071   5492291 sub _parse($self, $yaml) {
  6071         9919  
  6071         9528  
  6071         7594  
769 6071         8415 my @events;
770 6071         23009 YAML::LibYAML::API::XS::parse_events($yaml, \@events);
771 6071         294748 return \@events;
772             }
773              
774 0     0   0 sub _pp($event) {
  0         0  
  0         0  
775 0         0 my $name = $event->{name};
776 0         0 my $level = $event->{level};
777 0         0 $name =~ s/_event$//;
778 0         0 my $fmt = '%2d %-10s) %-14s';
779 0         0 my $indent = $level*2+2;
780 0         0 my $lstr = (' ' x $indent) . $level;
781             my @args = (
782             $event->{id}, $lstr,
783             $event->{start}->{line}, $event->{start}->{column},
784             $event->{end}->{line}, $event->{end}->{column},
785 0         0 $name,
786             );
787 0 0       0 if ($name =~ m/scalar|alias/) {
    0          
788 0         0 local $Data::Dumper::Useqq = 1;
789 0         0 my $str = Data::Dumper->Dump([$event->{value}], ['value']);
790 0         0 chomp $str;
791 0         0 $str =~ s/^\$value = //;
792 0         0 $fmt .= " %s";
793 0         0 push @args, $str;
794             }
795             elsif ($name =~ m/end/) {
796             }
797             else {
798             }
799 0         0 $fmt .= "\n";
800 0         0 printf $fmt, @args;
801             }
802              
803 0     0   0 sub _debug_lines($self) {
  0         0  
  0         0  
804 0         0 say "====================================";
805 0         0 say for @{ $self->{lines} };
  0         0  
806 0         0 say "====================================";
807             }
808              
809 6068     6068 1 32025777 sub highlight($self, $yaml, $type = 'ansi') {
  6068         10742  
  6068         9857  
  6068         9177  
  6068         7101  
810 6068         36570 my ($error, $tokens) = YAML::PP::Parser->yaml_to_tokens(string => $yaml);
811 6068 100       17073341 if ($error) {
812 242         2147 $tokens = [];
813 242         2266 my @lines = split m/(?<=\n)/, $yaml;
814 242         652 for my $line (@lines) {
815 694 50       1721 if ($line =~ s/( +\n)//) {
816 0         0 push @$tokens, { value => $line, name => 'PLAIN' };
817 0         0 push @$tokens, { value => $1, name => 'TRAILING_SPACE' };
818 0         0 next;
819             }
820 694         1857 push @$tokens, { value => $line, name => 'PLAIN' };
821             }
822             }
823 6068 50       17617 if ($type eq 'html') {
824 0         0 return YAML::PP::Highlight->htmlcolored($tokens);
825             }
826 6068         34088 return YAML::PP::Highlight->ansicolored($tokens);
827             }
828              
829             1;
830              
831             __END__