Skip to content

Commit

Permalink
Add compact option to force a more compact formatting (fixes #783).
Browse files Browse the repository at this point in the history
  • Loading branch information
andialbrecht committed Jul 12, 2024
1 parent 8b03427 commit bf74d8b
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 7 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Development Version
-------------------

Enhancements

* New "compact" option for formatter. If set, the formatter tries to produce
a more compact output by avoiding some line breaks (issue783).

Bug Fixes

* The strip comments filter was a bit greedy and removed too much
Expand All @@ -23,7 +28,7 @@ Notable Changes
https://github.com/andialbrecht/sqlparse/security/advisories/GHSA-2m57-hf25-phgg
The vulnerability was discovered by @uriyay-jfrog. Thanks for reporting!

Enhancements:
Enhancements

* Splitting statements now allows to remove the semicolon at the end.
Some database backends love statements without semicolon (issue742).
Expand Down
3 changes: 3 additions & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ The :meth:`~sqlparse.format` function accepts the following keyword arguments.
The column limit (in characters) for wrapping comma-separated lists. If unspecified, it
puts every item in the list on its own line.

``compact```
If ``True`` the formatter tries to produce more compact output.

``output_format``
If given the output is additionally formatted to be used as a variable
in a programming language. Allowed values are "python" and "php".
Expand Down
9 changes: 8 additions & 1 deletion sqlparse/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,14 @@ def create_parser():
default=False,
type=bool,
help='Insert linebreak before comma (default False)')


group.add_argument(
'--compact',
dest='compact',
default=False,
type=bool,
help='Try to produce more compact output (default False)')

group.add_argument(
'--encoding',
dest='encoding',
Expand Down
14 changes: 10 additions & 4 deletions sqlparse/filters/reindent.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class ReindentFilter:
def __init__(self, width=2, char=' ', wrap_after=0, n='\n',
comma_first=False, indent_after_first=False,
indent_columns=False):
indent_columns=False, compact=False):
self.n = n
self.width = width
self.char = char
Expand All @@ -21,6 +21,7 @@ def __init__(self, width=2, char=' ', wrap_after=0, n='\n',
self.wrap_after = wrap_after
self.comma_first = comma_first
self.indent_columns = indent_columns
self.compact = compact
self._curr_stmt = None
self._last_stmt = None
self._last_func = None
Expand Down Expand Up @@ -196,15 +197,20 @@ def _process_case(self, tlist):
with offset(self, self._get_offset(tlist[0])):
with offset(self, self._get_offset(first)):
for cond, value in iterable:
token = value[0] if cond is None else cond[0]
tlist.insert_before(token, self.nl())
str_cond = ''.join(str(x) for x in cond or [])
str_value = ''.join(str(x) for x in value)
end_pos = self.offset + 1 + len(str_cond) + len(str_value)
if (not self.compact
and end_pos > self.wrap_after):
token = value[0] if cond is None else cond[0]
tlist.insert_before(token, self.nl())

# Line breaks on group level are done. let's add an offset of
# len "when ", "then ", "else "
with offset(self, len("WHEN ")):
self._process_default(tlist)
end_idx, end = tlist.token_next_by(m=sql.Case.M_CLOSE)
if end_idx is not None:
if end_idx is not None and not self.compact:
tlist.insert_before(end_idx, self.nl())

def _process_values(self, tlist):
Expand Down
8 changes: 7 additions & 1 deletion sqlparse/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ def validate_options(options):
if comma_first not in [True, False]:
raise SQLParseError('comma_first requires a boolean value')
options['comma_first'] = comma_first

compact = options.get('compact', False)
if compact not in [True, False]:
raise SQLParseError('compact requires a boolean value')
options['compact'] = compact

right_margin = options.get('right_margin')
if right_margin is not None:
Expand Down Expand Up @@ -171,7 +176,8 @@ def build_filter_stack(stack, options):
indent_after_first=options['indent_after_first'],
indent_columns=options['indent_columns'],
wrap_after=options['wrap_after'],
comma_first=options['comma_first']))
comma_first=options['comma_first'],
compact=options['compact'],))

if options.get('reindent_aligned', False):
stack.enable_grouping()
Expand Down
11 changes: 11 additions & 0 deletions tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,3 +734,14 @@ def test_format_json_ops(): # issue542
"select foo->'bar', foo->'bar';", reindent=True)
expected = "select foo->'bar',\n foo->'bar';"
assert formatted == expected


@pytest.mark.parametrize('sql, expected_normal, expected_compact', [
('case when foo then 1 else bar end',
'case\n when foo then 1\n else bar\nend',
'case when foo then 1 else bar end')])
def test_compact(sql, expected_normal, expected_compact): # issue783
formatted_normal = sqlparse.format(sql, reindent=True)
formatted_compact = sqlparse.format(sql, reindent=True, compact=True)
assert formatted_normal == expected_normal
assert formatted_compact == expected_compact

0 comments on commit bf74d8b

Please sign in to comment.