Skip to content

Commit 80255c7

Browse files
authored
Improve the inline/columnar combination (#80)
* feat: Improve the inline/columnar combination Fix again the span computation, make so the compact representation does not behave weirdly when it needs to go columnar in subqueries. * feat: Add an option to consider JOINs top level or plain reserved newline * fix: Make sure UNION ALL is correctly parsed * fix: SELECT DISTINCT should have priority over SELECT
1 parent 31a2dfe commit 80255c7

File tree

5 files changed

+174
-82
lines changed

5 files changed

+174
-82
lines changed

src/formatter.rs

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::indentation::Indentation;
44
use crate::inline_block::InlineBlock;
55
use crate::params::Params;
66
use crate::tokenizer::{Token, TokenKind};
7-
use crate::{FormatOptions, QueryParams};
7+
use crate::{FormatOptions, QueryParams, SpanInfo};
88

99
// -- fmt: off
1010
// -- fmt: on
@@ -101,10 +101,6 @@ pub(crate) fn format(
101101
formatter.format_newline_reserved_word(token, &mut formatted_query);
102102
formatter.previous_reserved_word = Some(token);
103103
}
104-
TokenKind::Join => {
105-
formatter.format_newline_reserved_word(token, &mut formatted_query);
106-
formatter.previous_reserved_word = Some(token);
107-
}
108104
TokenKind::Reserved => {
109105
formatter.format_with_spaces(token, &mut formatted_query);
110106
formatter.previous_reserved_word = Some(token);
@@ -167,14 +163,8 @@ impl<'a> Formatter<'a> {
167163
indentation: Indentation::new(options),
168164
inline_block: InlineBlock::new(
169165
options.max_inline_block,
170-
match (options.max_inline_arguments, options.max_inline_top_level) {
171-
(Some(max_inline_args), Some(max_inline_top)) => {
172-
max_inline_args.min(max_inline_top)
173-
}
174-
(Some(max_inline_args), None) => max_inline_args,
175-
(None, Some(max_inline_top)) => max_inline_top,
176-
(None, None) => 0,
177-
},
166+
options.max_inline_arguments.unwrap_or(0),
167+
options.max_inline_top_level.unwrap_or(0),
178168
),
179169
block_level: 0,
180170
}
@@ -215,12 +205,12 @@ impl<'a> Formatter<'a> {
215205
self.add_new_line(query);
216206
}
217207

218-
// if we are inside an inline block we decide our behaviour as if we are an inline argument
219-
fn top_level_behavior(&self) -> (bool, bool) {
220-
let span_len = self.top_level_tokens_span();
208+
// if we are inside an inline block we decide our behaviour as if were inline
209+
fn top_level_behavior(&self, span_info: &SpanInfo) -> (bool, bool) {
210+
let span_len = span_info.full_span;
221211
let block_len = self.inline_block.cur_len();
222212
if block_len > 0 {
223-
let limit = self.options.max_inline_arguments.unwrap_or(0);
213+
let limit = self.options.max_inline_top_level.unwrap_or(0);
224214
(limit < block_len, limit < span_len)
225215
} else {
226216
(
@@ -233,24 +223,25 @@ impl<'a> Formatter<'a> {
233223
}
234224

235225
fn format_top_level_reserved_word(&mut self, token: &Token<'_>, query: &mut String) {
236-
let span_len = self.top_level_tokens_span();
237-
let (newline_before, newline_after) = self.top_level_behavior();
226+
let span_info = self.top_level_tokens_info();
227+
let (newline_before, newline_after) = self.top_level_behavior(&span_info);
238228

239229
if newline_before {
240230
self.indentation.decrease_top_level();
241231
self.add_new_line(query);
242232
}
243233
query.push_str(&self.equalize_whitespace(&self.format_reserved_word(token.value)));
244234
if newline_after {
245-
self.indentation.increase_top_level(span_len);
235+
self.indentation.increase_top_level(span_info);
246236
self.add_new_line(query);
247237
} else {
248238
query.push(' ');
249239
}
250240
}
251241

252242
fn format_top_level_reserved_word_no_indent(&mut self, token: &Token<'_>, query: &mut String) {
253-
let (newline_before, newline_after) = self.top_level_behavior();
243+
let span_info = self.top_level_tokens_info();
244+
let (newline_before, newline_after) = self.top_level_behavior(&span_info);
254245

255246
if newline_before {
256247
self.indentation.decrease_top_level();
@@ -263,10 +254,11 @@ impl<'a> Formatter<'a> {
263254
}
264255

265256
fn format_newline_reserved_word(&mut self, token: &Token<'_>, query: &mut String) {
266-
if self
267-
.options
268-
.max_inline_arguments
269-
.map_or(true, |limit| limit < self.indentation.top_level_span())
257+
if !self.inline_block.is_active()
258+
&& self
259+
.options
260+
.max_inline_arguments
261+
.map_or(true, |limit| limit < self.indentation.span())
270262
{
271263
self.add_new_line(query);
272264
} else {
@@ -406,7 +398,7 @@ impl<'a> Formatter<'a> {
406398

407399
if matches!((self.previous_top_level_reserved_word, self.options.max_inline_arguments),
408400
(Some(word), Some(limit)) if ["select", "from"].contains(&word.value.to_lowercase().as_str()) &&
409-
limit > self.indentation.top_level_span())
401+
limit > self.indentation.span())
410402
{
411403
return;
412404
}
@@ -528,28 +520,33 @@ impl<'a> Formatter<'a> {
528520
}
529521
}
530522

531-
fn top_level_tokens_span(&self) -> usize {
523+
fn top_level_tokens_info(&self) -> SpanInfo {
532524
let mut block_level = self.block_level;
525+
let mut full_span = 0;
533526

534-
self.tokens[self.index..]
535-
.iter()
536-
.skip(1)
537-
.take_while(|token| match token.kind {
527+
for token in self.tokens[self.index..].iter().skip(1) {
528+
match token.kind {
538529
TokenKind::OpenParen => {
539530
block_level += 1;
540-
true
541531
}
542532
TokenKind::CloseParen => {
543533
block_level = block_level.saturating_sub(1);
544-
block_level > self.block_level
534+
if block_level < self.block_level {
535+
break;
536+
}
545537
}
546538
TokenKind::ReservedTopLevel | TokenKind::ReservedTopLevelNoIndent => {
547-
block_level != self.block_level
539+
if block_level == self.block_level {
540+
break;
541+
}
548542
}
549-
_ => true,
550-
})
551-
.map(|token| token.value.len())
552-
.sum()
543+
_ => {}
544+
}
545+
546+
full_span += token.value.len();
547+
}
548+
549+
SpanInfo { full_span }
553550
}
554551

555552
fn format_no_change(&self, token: &Token<'_>, query: &mut String) {

src/indentation.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use crate::{FormatOptions, Indent};
1+
use crate::{FormatOptions, Indent, SpanInfo};
22

33
pub(crate) struct Indentation<'a> {
44
options: &'a FormatOptions<'a>,
55
indent_types: Vec<IndentType>,
6-
top_level_span: Vec<usize>,
6+
top_level_span: Vec<SpanInfo>,
77
}
88

99
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -30,7 +30,7 @@ impl<'a> Indentation<'a> {
3030
}
3131
}
3232

33-
pub fn increase_top_level(&mut self, span: usize) {
33+
pub fn increase_top_level(&mut self, span: SpanInfo) {
3434
self.indent_types.push(IndentType::TopLevel);
3535
self.top_level_span.push(span);
3636
}
@@ -60,7 +60,8 @@ impl<'a> Indentation<'a> {
6060
self.top_level_span.clear();
6161
}
6262

63-
pub fn top_level_span(&self) -> usize {
64-
self.top_level_span.last().map_or(0, |span| *span)
63+
/// The full span between two top level tokens
64+
pub fn span(&self) -> usize {
65+
self.top_level_span.last().map_or(0, |span| span.full_span)
6566
}
6667
}

src/inline_block.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ use crate::tokenizer::{Token, TokenKind};
33
pub(crate) struct BlockInfo {
44
length: usize,
55
has_forbidden_tokens: bool,
6+
has_reseved_tokens: bool,
67
top_level_token_span: usize,
78
}
89

910
pub(crate) struct InlineBlock {
1011
level: usize,
1112
inline_max_length: usize,
12-
newline_on_reserved_limit: usize,
13+
reserved_limit: usize,
14+
reserved_top_limit: usize,
1315
info: Vec<BlockInfo>,
1416
}
1517

@@ -19,24 +21,27 @@ impl Default for InlineBlock {
1921
info: Vec::new(),
2022
level: 0,
2123
inline_max_length: 50,
22-
newline_on_reserved_limit: 0,
24+
reserved_limit: 0,
25+
reserved_top_limit: 0,
2326
}
2427
}
2528
}
2629

2730
impl InlineBlock {
28-
pub fn new(inline_max_length: usize, newline_on_reserved_limit: usize) -> Self {
31+
pub fn new(inline_max_length: usize, reserved_limit: usize, reserved_top_limit: usize) -> Self {
2932
InlineBlock {
3033
inline_max_length,
31-
newline_on_reserved_limit,
34+
reserved_limit,
35+
reserved_top_limit,
3236
..Default::default()
3337
}
3438
}
3539

3640
fn is_inline_block(&self, info: &BlockInfo) -> bool {
3741
!info.has_forbidden_tokens
3842
&& info.length <= self.inline_max_length
39-
&& info.top_level_token_span <= self.newline_on_reserved_limit
43+
&& info.top_level_token_span <= self.reserved_top_limit
44+
&& (!info.has_reseved_tokens || info.length <= self.reserved_limit)
4045
}
4146

4247
pub fn begin_if_possible(&mut self, tokens: &[Token<'_>], index: usize) {
@@ -74,10 +79,10 @@ impl InlineBlock {
7479
let mut start_top_level = -1;
7580
let mut start_span = 0;
7681
let mut has_forbidden_tokens = false;
82+
let mut has_reseved_tokens = false;
7783

7884
for token in &tokens[index..] {
7985
length += token.value.len();
80-
8186
match token.kind {
8287
TokenKind::ReservedTopLevel | TokenKind::ReservedTopLevelNoIndent => {
8388
if start_top_level != -1 {
@@ -90,6 +95,9 @@ impl InlineBlock {
9095
start_span = length;
9196
}
9297
}
98+
TokenKind::ReservedNewline => {
99+
has_reseved_tokens = true;
100+
}
93101
TokenKind::OpenParen => {
94102
level += 1;
95103
}
@@ -111,6 +119,7 @@ impl InlineBlock {
111119
BlockInfo {
112120
length,
113121
has_forbidden_tokens,
122+
has_reseved_tokens,
114123
top_level_token_span,
115124
}
116125
}
@@ -119,11 +128,5 @@ impl InlineBlock {
119128
token.kind == TokenKind::LineComment
120129
|| token.kind == TokenKind::BlockComment
121130
|| token.value == ";"
122-
|| if self.newline_on_reserved_limit == 0 {
123-
token.kind == TokenKind::ReservedTopLevel
124-
|| token.kind == TokenKind::ReservedNewline
125-
} else {
126-
false
127-
}
128131
}
129132
}

0 commit comments

Comments
 (0)