diff --git a/lib/pgFormatter/Beautify.pm b/lib/pgFormatter/Beautify.pm index 5e25217..c526f41 100755 --- a/lib/pgFormatter/Beautify.pm +++ b/lib/pgFormatter/Beautify.pm @@ -168,6 +168,7 @@ Takes options as hash. Following options are recognized: =item * compact_clause_body - keep the first element of a clause on the keyword line +=item * join_on_indent - indent the ON clause continuation lines (AND/OR) of a multi-keyword join (e.g. LEFT OUTER JOIN) one level below the join keyword =item * isolate_semicolon - place the statement terminating semicolon on its own line when the statement spans several lines @@ -189,6 +190,7 @@ sub new { $self->set_defaults(); for my $key ( + qw( query spaces space break wrap keywords functions rules uc_keywords uc_functions uc_types uc_identifiers no_comments no_grouping placeholder multiline separator comma comma_break format colorize format_type wrap_limit wrap_after wrap_comment numbering redshift no_extra_line keep_newline no_space_function compact_clause_body join_on_indent redundant_parenthesis vertical_align) qw( query spaces space break wrap keywords functions rules uc_keywords uc_functions uc_types uc_identifiers no_comments no_grouping placeholder multiline separator comma comma_break format colorize format_type wrap_limit wrap_after wrap_comment numbering redshift no_extra_line keep_newline no_space_function compact_clause_body isolate_semicolon redundant_parenthesis vertical_align) ) { @@ -4119,7 +4121,14 @@ sub beautify { and ( not defined $last or uc($last) ne 'MATCH' ) ) { $self->{'no_break'} = 0; - if ( !$self->{'_is_in_join'} and ( defined $last and $last ne ')' ) ) { + # When join_on_indent is enabled, only step back once per join clause: + # a join spelled with several keywords, such as LEFT OUTER JOIN, must + # not step back once per keyword or it loses one indentation level. + if ( !$self->{'_is_in_join'} + and ( defined $last and $last ne ')' ) + and ( !$self->{'join_on_indent'} + or $last !~ /^(?:LEFT|RIGHT|FULL|INNER|OUTER|CROSS|NATURAL)$/i ) ) + { $self->_back( $token, $last ) if ($#{ $self->{'_level_stack'} } < 0 or $self->{'_level'} > $self->{'_level_stack'}[-1]+1); } if ( $self->{'_has_over_in_join'} ) { @@ -5760,6 +5769,7 @@ Currently defined defaults: =item compact_clause_body => 0 +=item join_on_indent => 0 =item isolate_semicolon => 0 =item redundant_parenthesis => 0 @@ -5809,6 +5819,7 @@ sub set_defaults { $self->{'keep_newline'} = 0; $self->{'no_space_function'} = 0; $self->{'compact_clause_body'} = 0; + $self->{'join_on_indent'} = 0; $self->{'isolate_semicolon'} = 0; $self->{'_stmt_start_offset'} = 0; $self->{'_stmt_started'} = 0; diff --git a/lib/pgFormatter/CLI.pm b/lib/pgFormatter/CLI.pm index 1831adb..1d8a625 100755 --- a/lib/pgFormatter/CLI.pm +++ b/lib/pgFormatter/CLI.pm @@ -131,6 +131,7 @@ sub beautify { $args{'extra_keyword'} = $self->{'cfg'}->{'extra-keyword'}; $args{'no_space_function'} = $self->{'cfg'}->{'no-space-function'}; $args{'compact_clause_body'} = $self->{'cfg'}->{'compact-clause-body'}; + $args{'join_on_indent'} = $self->{'cfg'}->{'join-on-indent'}; $args{'isolate_semicolon'} = $self->{'cfg'}->{'isolate-semicolon'}; $args{'redundant_parenthesis'} = $self->{'cfg'}->{'redundant-parenthesis'}; $args{'vertical_align'} = $self->{'cfg'}->{'vertical-align'}; @@ -334,6 +335,9 @@ Options: --compact-clause-body : keep the first element of a FROM, WHERE, SET, RETURNING, HAVING or VALUES clause on the same line as the keyword, and the body of a CASE ... THEN on the same line as THEN. + --join-on-indent : indent the ON clause continuation lines (AND/OR) of a + multi-keyword join (e.g. LEFT OUTER JOIN) one level + below the join keyword. --isolate-semicolon : place the statement terminating semicolon on its own line instead of appending it to the last token, unless the whole statement fits on a single line. @@ -407,6 +411,7 @@ sub get_command_line_args { 'wrap-limit|w=i', 'wrap-after|W=i', 'inplace|i!', 'extra-function=s', 'extra-keyword=s', 'no-space-function!', + 'compact-clause-body!', 'join-on-indent!', 'ident-case|I=i', 'compact-clause-body!', 'ident-case|I=i', 'isolate-semicolon!', 'redundant-parenthesis!', 'vertical-align!', ); diff --git a/t/02_regress.t b/t/02_regress.t index 03c49e8..f8d242c 100755 --- a/t/02_regress.t +++ b/t/02_regress.t @@ -32,6 +32,7 @@ foreach my $f (@files) $opt = "--compact-clause-body" if ($f =~ m#/ex82.sql$#); $opt = "--no-space-function" if ($f =~ m#/ex83.sql$#); $opt = "--ident-case 2 -f 1 -U 1" if ($f =~ m#/ex84\.sql$#); + $opt = "--join-on-indent" if ($f =~ m#/ex85.sql$#); $opt = "--isolate-semicolon" if ($f =~ m#/ex85.sql$#); if ($f =~ m#/ex61.sql$#) { diff --git a/t/test-files/ex85.sql b/t/test-files/ex85.sql index f92099e..b679f2f 100644 --- a/t/test-files/ex85.sql +++ b/t/test-files/ex85.sql @@ -1,3 +1,28 @@ +SELECT + tyap0.tyap_code + , appel.frap_tyap_code +FROM + tyap + LEFT OUTER JOIN ab_frap@link_serveur appel ON appel.frap_tyap_code = tyap.tyap_code + AND appel.frap_imme_no = tyap.tyap_imme_no + LEFT OUTER JOIN bud ON bud.budg_tyap_direct = tyap.tyap_direct; + +SELECT + a.id +FROM + a + INNER JOIN b ON b.id = a.id + CROSS JOIN c ON true + NATURAL JOIN d ON true + RIGHT JOIN e ON e.id = a.id + FULL JOIN f ON f.id = a.id; + +SELECT + x.v +FROM + x + JOIN y ON y.k = x.k + LEFT JOIN z ON z.k = x.k; DO $$ BEGIN IF true THEN diff --git a/t/test-files/expected/ex85.sql b/t/test-files/expected/ex85.sql index 0deaa87..e6daab5 100644 --- a/t/test-files/expected/ex85.sql +++ b/t/test-files/expected/ex85.sql @@ -1,3 +1,28 @@ +SELECT + tyap0.tyap_code, + appel.frap_tyap_code +FROM + tyap + LEFT OUTER JOIN ab_frap@link_serveur appel ON appel.frap_tyap_code = tyap.tyap_code + AND appel.frap_imme_no = tyap.tyap_imme_no + LEFT OUTER JOIN bud ON bud.budg_tyap_direct = tyap.tyap_direct; + +SELECT + a.id +FROM + a + INNER JOIN b ON b.id = a.id + CROSS JOIN c ON TRUE + NATURAL JOIN d ON TRUE + RIGHT JOIN e ON e.id = a.id + FULL JOIN f ON f.id = a.id; + +SELECT + x.v +FROM + x + JOIN y ON y.k = x.k + LEFT JOIN z ON z.k = x.k; DO $$ BEGIN IF TRUE THEN