Skip to content

Commit d49ed44

Browse files
authored
feat: More formatting options (#74)
* chore: Add pretty_assertions * feat: provide an option to format to a single line * feat: Make the inline blocks limit tunable * feat: Add an option to keep arguments inline * feat: Add an option to inline over the top level tokens
1 parent 211fbb8 commit d49ed44

File tree

4 files changed

+281
-15
lines changed

4 files changed

+281
-15
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ winnow = { version = "0.6.20", features = ["simd"] }
1919
[dev-dependencies]
2020
criterion = "0.4"
2121
indoc = "2.0"
22+
pretty_assertions = "1.4.1"
2223

2324
[[bench]]
2425
name = "bench"

src/formatter.rs

+67-9
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,12 @@ pub(crate) fn format(
9090
TokenKind::ReservedTopLevel => {
9191
formatter.format_top_level_reserved_word(token, &mut formatted_query);
9292
formatter.previous_reserved_word = Some(token);
93+
formatter.previous_top_level_reserved_word = Some(token);
9394
}
9495
TokenKind::ReservedTopLevelNoIndent => {
9596
formatter.format_top_level_reserved_word_no_indent(token, &mut formatted_query);
9697
formatter.previous_reserved_word = Some(token);
98+
formatter.previous_top_level_reserved_word = Some(token);
9799
}
98100
TokenKind::ReservedNewline => {
99101
formatter.format_newline_reserved_word(token, &mut formatted_query);
@@ -140,27 +142,34 @@ pub(crate) fn format(
140142
struct Formatter<'a> {
141143
index: usize,
142144
previous_reserved_word: Option<&'a Token<'a>>,
145+
previous_top_level_reserved_word: Option<&'a Token<'a>>,
143146
tokens: &'a [Token<'a>],
144147
params: Params<'a>,
145148
options: &'a FormatOptions<'a>,
146149
indentation: Indentation<'a>,
147150
inline_block: InlineBlock,
151+
line_start: usize,
148152
}
149153

150154
impl<'a> Formatter<'a> {
151155
fn new(tokens: &'a [Token<'a>], params: &'a QueryParams, options: &'a FormatOptions) -> Self {
152156
Formatter {
153157
index: 0,
154158
previous_reserved_word: None,
159+
previous_top_level_reserved_word: None,
155160
tokens,
156161
params: Params::new(params),
157162
options,
158163
indentation: Indentation::new(options),
159-
inline_block: InlineBlock::new(),
164+
inline_block: InlineBlock::new(
165+
options.max_inline_block,
166+
options.max_inline_arguments.is_none(),
167+
),
168+
line_start: 0,
160169
}
161170
}
162171

163-
fn format_line_comment(&self, token: &Token<'_>, query: &mut String) {
172+
fn format_line_comment(&mut self, token: &Token<'_>, query: &mut String) {
164173
let is_whitespace_followed_by_special_token =
165174
self.next_token(1).map_or(false, |current_token| {
166175
current_token.kind == TokenKind::Whitespace
@@ -189,7 +198,7 @@ impl<'a> Formatter<'a> {
189198
self.trim_all_spaces_end(query);
190199
query.push_str("::");
191200
}
192-
fn format_block_comment(&self, token: &Token<'_>, query: &mut String) {
201+
fn format_block_comment(&mut self, token: &Token<'_>, query: &mut String) {
193202
self.add_new_line(query);
194203
query.push_str(&self.indent_comment(token.value));
195204
self.add_new_line(query);
@@ -200,7 +209,16 @@ impl<'a> Formatter<'a> {
200209
self.add_new_line(query);
201210
self.indentation.increase_top_level();
202211
query.push_str(&self.equalize_whitespace(&self.format_reserved_word(token.value)));
203-
self.add_new_line(query);
212+
let len = self.top_level_tokens_span();
213+
if self
214+
.options
215+
.max_inline_top_level
216+
.map_or(true, |limit| limit < len)
217+
{
218+
self.add_new_line(query);
219+
} else {
220+
query.push(' ');
221+
}
204222
}
205223

206224
fn format_top_level_reserved_word_no_indent(&mut self, token: &Token<'_>, query: &mut String) {
@@ -210,8 +228,17 @@ impl<'a> Formatter<'a> {
210228
self.add_new_line(query);
211229
}
212230

213-
fn format_newline_reserved_word(&self, token: &Token<'_>, query: &mut String) {
214-
self.add_new_line(query);
231+
fn format_newline_reserved_word(&mut self, token: &Token<'_>, query: &mut String) {
232+
if self
233+
.options
234+
.max_inline_arguments
235+
.map_or(true, |limit| limit < self.line_len_next(query))
236+
{
237+
self.add_new_line(query);
238+
} else {
239+
self.trim_spaces_end(query);
240+
query.push(' ');
241+
}
215242
query.push_str(&self.equalize_whitespace(&self.format_reserved_word(token.value)));
216243
query.push(' ');
217244
}
@@ -303,7 +330,13 @@ impl<'a> Formatter<'a> {
303330

304331
if self.inline_block.is_active() {
305332
self.inline_block.end();
306-
self.format_with_space_after(&token, query);
333+
if token.value.to_lowercase() == "end" {
334+
self.trim_spaces_end(query);
335+
query.push(' ');
336+
self.format_with_spaces(&token, query);
337+
} else {
338+
self.format_with_space_after(&token, query);
339+
}
307340
} else {
308341
self.indentation.decrease_block_level();
309342
self.add_new_line(query);
@@ -317,7 +350,7 @@ impl<'a> Formatter<'a> {
317350
}
318351

319352
// Commas start a new line (unless within inline parentheses or SQL "LIMIT" clause)
320-
fn format_comma(&self, token: &Token<'_>, query: &mut String) {
353+
fn format_comma(&mut self, token: &Token<'_>, query: &mut String) {
321354
self.trim_spaces_end(query);
322355
query.push_str(token.value);
323356
query.push(' ');
@@ -332,6 +365,12 @@ impl<'a> Formatter<'a> {
332365
{
333366
return;
334367
}
368+
369+
if matches!((self.previous_top_level_reserved_word, self.options.max_inline_arguments),
370+
(Some(word), Some(limit)) if word.value.to_lowercase() == "select" && limit > self.line_len_next(query))
371+
{
372+
return;
373+
}
335374
self.add_new_line(query);
336375
}
337376

@@ -355,11 +394,16 @@ impl<'a> Formatter<'a> {
355394
}
356395
}
357396

358-
fn add_new_line(&self, query: &mut String) {
397+
fn add_new_line(&mut self, query: &mut String) {
359398
self.trim_spaces_end(query);
399+
if self.options.inline {
400+
query.push(' ');
401+
return;
402+
}
360403
if !query.ends_with('\n') {
361404
query.push('\n');
362405
}
406+
self.line_start = query.len();
363407
query.push_str(&self.indentation.get_indent());
364408
}
365409

@@ -446,6 +490,20 @@ impl<'a> Formatter<'a> {
446490
}
447491
}
448492

493+
fn line_len_next(&self, query: &str) -> usize {
494+
query.len() - self.line_start + self.next_token(1).map_or(0, |t| t.value.len())
495+
}
496+
497+
fn top_level_tokens_span(&self) -> usize {
498+
assert_eq!(self.tokens[self.index].kind, TokenKind::ReservedTopLevel);
499+
500+
self.tokens[self.index..]
501+
.iter()
502+
.skip(1)
503+
.map(|token| token.value.len())
504+
.sum()
505+
}
506+
449507
fn format_no_change(&self, token: &Token<'_>, query: &mut String) {
450508
query.push_str(token.value);
451509
}

src/inline_block.rs

+25-6
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,27 @@ use crate::tokenizer::{Token, TokenKind};
22

33
pub(crate) struct InlineBlock {
44
level: usize,
5+
inline_max_length: usize,
6+
newline_on_reserved: bool,
7+
}
8+
9+
impl Default for InlineBlock {
10+
fn default() -> Self {
11+
InlineBlock {
12+
level: 0,
13+
inline_max_length: 50,
14+
newline_on_reserved: true,
15+
}
16+
}
517
}
618

719
impl InlineBlock {
8-
pub fn new() -> Self {
9-
InlineBlock { level: 0 }
20+
pub fn new(inline_max_length: usize, newline_on_reserved: bool) -> Self {
21+
InlineBlock {
22+
level: 0,
23+
inline_max_length,
24+
newline_on_reserved,
25+
}
1026
}
1127

1228
pub fn begin_if_possible(&mut self, tokens: &[Token<'_>], index: usize) {
@@ -28,16 +44,14 @@ impl InlineBlock {
2844
}
2945

3046
fn is_inline_block(&self, tokens: &[Token<'_>], index: usize) -> bool {
31-
const INLINE_MAX_LENGTH: usize = 50;
32-
3347
let mut length = 0;
3448
let mut level = 0;
3549

3650
for token in &tokens[index..] {
3751
length += token.value.len();
3852

3953
// Overran max length
40-
if length > INLINE_MAX_LENGTH {
54+
if length > self.inline_max_length {
4155
return false;
4256
}
4357
if token.kind == TokenKind::OpenParen {
@@ -59,9 +73,14 @@ impl InlineBlock {
5973

6074
fn is_forbidden_token(&self, token: &Token<'_>) -> bool {
6175
token.kind == TokenKind::ReservedTopLevel
62-
|| token.kind == TokenKind::ReservedNewline
6376
|| token.kind == TokenKind::LineComment
6477
|| token.kind == TokenKind::BlockComment
6578
|| token.value == ";"
79+
|| if self.newline_on_reserved {
80+
token.kind == TokenKind::ReservedNewline
81+
} else {
82+
false
83+
}
84+
|| token.value.to_lowercase() == "case"
6685
}
6786
}

0 commit comments

Comments
 (0)