-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: Upgrade to Winnow 0.6.26 * refactor: Resolve deprecations * refactor: Switch from Parser to ModalParser * chore: Upgrade to Winnow 0.7.0 * refactor: Remove use of ErrMode * fix: Fix the span computation and usage * feat: Extend ALTER TABLE actions support * feat: Always keep track of the indentation level of blocks This way is possible to have inlined blocks that break if the argument limit is hit. * feat!: Consider SELECT ALL/DISTINCT a single token * feat: Give joins their own token kind * fix: Allow inline case-end * fix: Improve inlining of subqueries
- Loading branch information
Showing
4 changed files
with
197 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,86 +1,129 @@ | ||
use crate::tokenizer::{Token, TokenKind}; | ||
|
||
pub(crate) struct BlockInfo { | ||
length: usize, | ||
has_forbidden_tokens: bool, | ||
top_level_token_span: usize, | ||
} | ||
|
||
pub(crate) struct InlineBlock { | ||
level: usize, | ||
inline_max_length: usize, | ||
newline_on_reserved: bool, | ||
newline_on_reserved_limit: usize, | ||
info: Vec<BlockInfo>, | ||
} | ||
|
||
impl Default for InlineBlock { | ||
fn default() -> Self { | ||
InlineBlock { | ||
info: Vec::new(), | ||
level: 0, | ||
inline_max_length: 50, | ||
newline_on_reserved: true, | ||
newline_on_reserved_limit: 0, | ||
} | ||
} | ||
} | ||
|
||
impl InlineBlock { | ||
pub fn new(inline_max_length: usize, newline_on_reserved: bool) -> Self { | ||
pub fn new(inline_max_length: usize, newline_on_reserved_limit: usize) -> Self { | ||
InlineBlock { | ||
level: 0, | ||
inline_max_length, | ||
newline_on_reserved, | ||
newline_on_reserved_limit, | ||
..Default::default() | ||
} | ||
} | ||
|
||
fn is_inline_block(&self, info: &BlockInfo) -> bool { | ||
!info.has_forbidden_tokens | ||
&& info.length <= self.inline_max_length | ||
&& info.top_level_token_span <= self.newline_on_reserved_limit | ||
} | ||
|
||
pub fn begin_if_possible(&mut self, tokens: &[Token<'_>], index: usize) { | ||
if self.level == 0 && self.is_inline_block(tokens, index) { | ||
let info = self.build_info(tokens, index); | ||
if self.level == 0 && self.is_inline_block(&info) { | ||
self.level = 1; | ||
} else if self.level > 0 { | ||
self.level += 1; | ||
} else { | ||
self.level = 0; | ||
} | ||
if self.level > 0 { | ||
self.info.push(info); | ||
} | ||
} | ||
|
||
pub fn end(&mut self) { | ||
self.info.pop(); | ||
self.level -= 1; | ||
} | ||
|
||
pub fn is_active(&self) -> bool { | ||
self.level > 0 | ||
} | ||
|
||
fn is_inline_block(&self, tokens: &[Token<'_>], index: usize) -> bool { | ||
/// Get the current inline block length | ||
pub fn cur_len(&self) -> usize { | ||
self.info.last().map_or(0, |info| info.length) | ||
} | ||
|
||
fn build_info(&self, tokens: &[Token<'_>], index: usize) -> BlockInfo { | ||
let mut length = 0; | ||
let mut level = 0; | ||
let mut top_level_token_span = 0; | ||
let mut start_top_level = -1; | ||
let mut start_span = 0; | ||
let mut has_forbidden_tokens = false; | ||
|
||
for token in &tokens[index..] { | ||
length += token.value.len(); | ||
|
||
// Overran max length | ||
if length > self.inline_max_length { | ||
return false; | ||
} | ||
if token.kind == TokenKind::OpenParen { | ||
level += 1; | ||
} else if token.kind == TokenKind::CloseParen { | ||
level -= 1; | ||
if level == 0 { | ||
return true; | ||
match token.kind { | ||
TokenKind::ReservedTopLevel | TokenKind::ReservedTopLevelNoIndent => { | ||
if start_top_level != -1 { | ||
if start_top_level == level { | ||
top_level_token_span = top_level_token_span.max(length - start_span); | ||
start_top_level = -1; | ||
} | ||
} else { | ||
start_top_level = level; | ||
start_span = length; | ||
} | ||
} | ||
TokenKind::OpenParen => { | ||
level += 1; | ||
} | ||
TokenKind::CloseParen => { | ||
level -= 1; | ||
if level == 0 { | ||
break; | ||
} | ||
} | ||
_ => {} | ||
} | ||
|
||
if self.is_forbidden_token(token) { | ||
return false; | ||
has_forbidden_tokens = true; | ||
} | ||
} | ||
|
||
false | ||
// broken syntax let's try our best | ||
BlockInfo { | ||
length, | ||
has_forbidden_tokens, | ||
top_level_token_span, | ||
} | ||
} | ||
|
||
fn is_forbidden_token(&self, token: &Token<'_>) -> bool { | ||
token.kind == TokenKind::ReservedTopLevel | ||
|| token.kind == TokenKind::LineComment | ||
token.kind == TokenKind::LineComment | ||
|| token.kind == TokenKind::BlockComment | ||
|| token.value == ";" | ||
|| if self.newline_on_reserved { | ||
token.kind == TokenKind::ReservedNewline | ||
|| if self.newline_on_reserved_limit == 0 { | ||
token.kind == TokenKind::ReservedTopLevel | ||
|| token.kind == TokenKind::ReservedNewline | ||
} else { | ||
false | ||
} | ||
|| ["case", "end"].contains(&token.value.to_lowercase().as_str()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.