Commit | Line | Data |
77ec9556 |
1 | package SQL::Abstract::Formatter; |
2 | |
3 | require SQL::Abstract::Parts; # it loads us too, don't cross the streams |
4 | |
5 | use Moo; |
6 | |
7 | has indent_by => (is => 'ro', default => ' '); |
8 | has max_width => (is => 'ro', default => 78); |
9 | |
10 | sub _join { |
11 | shift; |
b349d2fa |
12 | return SQL::Abstract::Parts::stringify(\@_); |
77ec9556 |
13 | } |
14 | |
15 | sub format { |
16 | my ($self, $join, @parts) = @_; |
b7516a3a |
17 | $self->_fold_sql('', '', @{$self->_simplify($join, @parts)}); |
18 | } |
19 | |
20 | sub _simplify { |
21 | my ($self, $join, @parts) = @_; |
22 | return '' unless @parts; |
23 | return $parts[0] if @parts == 1 and !ref($parts[0]); |
24 | return $self->_simplify(@{$parts[0]}) if @parts == 1; |
25 | return [ $join, map ref() ? $self->_simplify(@$_) : $_, @parts ]; |
26 | } |
27 | |
28 | sub _fold_sql { |
b7516a3a |
29 | my ($self, $indent0, $indent, $join, @parts) = @_; |
30 | my @res; |
31 | my $w = $self->max_width; |
32 | my $join_len = 0; |
33 | (s/, \Z/,\n/ and $join_len = 1) |
34 | or s/\A /\n/ |
35 | or $_ = "\n" |
36 | for my $line_join = $join; |
37 | my ($nl_pre, $nl_post) = split "\n", $line_join; |
1b20ff8d |
38 | my $line_orig = my $line = $indent0; |
b7516a3a |
39 | my $next_indent = $indent.$self->indent_by; |
1b20ff8d |
40 | my $line_proto = $indent.$nl_post; |
b7516a3a |
41 | PART: foreach my $idx (0..$#parts) { |
b7516a3a |
42 | my $p = $parts[$idx]; |
9b0bc0ac |
43 | #::DwarnT STARTPART => $p, \@res, $line, $line_orig; |
1b20ff8d |
44 | my $pre = ($line ne $line_orig ? $join : ''); |
b7516a3a |
45 | my $j_part = $pre.(my $j = ref($p) ? $self->_join(@$p) : $p); |
46 | if (length($j_part) + length($line) + $join_len <= $w) { |
47 | $line .= $j_part; |
3203315e |
48 | next PART; |
49 | } |
f17bb26d |
50 | my $innerdent = @res |
51 | ? $next_indent |
52 | : $indent0.$self->indent_by; |
3203315e |
53 | if (ref($p) and $p->[1] eq '(' and $p->[-1] eq ')') { |
54 | my $already = !($line eq $indent0 or $line eq $line_orig); |
55 | push @res, $line.($already ? $join : '').'('."\n"; |
56 | my (undef, undef, $inner) = @$p; |
4be609c3 |
57 | my $folded = $self->_fold_sql($innerdent, $innerdent, @$inner); |
cb3a6230 |
58 | $folded =~ s/\n\Z//; |
3203315e |
59 | push @res, $folded."\n"; |
60 | $line_orig = $line |
2d4a4d5f |
61 | = $indent0.')'.($idx == $#parts ? '' : $join); |
3203315e |
62 | next PART; |
63 | } |
e152bc5a |
64 | if ($line ne $line_orig) { |
65 | push @res, $line.($idx == $#parts ? '' : $nl_pre)."\n"; |
66 | } |
3203315e |
67 | if (length($line = $line_proto.$j) <= $w) { |
3203315e |
68 | next PART; |
b7516a3a |
69 | } |
3203315e |
70 | my $folded = $self->_fold_sql($line_proto, $innerdent, @$p); |
71 | $folded =~ s/\n\Z//; |
e152bc5a |
72 | push @res, $folded.($idx == $#parts ? '' : $nl_pre)."\n"; |
3203315e |
73 | $line_orig = $line = $idx == $#parts ? '' : $line_proto; |
9b0bc0ac |
74 | } continue { |
75 | #::DwarnT ENDPART => $parts[$idx], \@res, $line, $line_orig; |
b7516a3a |
76 | } |
b349d2fa |
77 | return join '', @res, $line; |
77ec9556 |
78 | } |
79 | |
80 | 1; |