Skip to content

Commit 226f8ac

Browse files
committed
자동포멧팅 기능 개선
1 parent 974738f commit 226f8ac

2 files changed

Lines changed: 338 additions & 64 deletions

File tree

src/sql_delimiter.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,38 @@ impl DelimiterLineStartSnapshot {
4646
pub(crate) fn frame_state(&self) -> DelimiterFrameState {
4747
DelimiterFrameState {
4848
stack: self.visible_frames.clone(),
49+
close_generations_by_resulting_depth: None,
4950
}
5051
}
5152
}
5253

54+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
55+
pub(crate) struct DelimiterBoundary {
56+
depth: usize,
57+
close_generation_below: u64,
58+
}
59+
60+
impl DelimiterBoundary {
61+
#[inline]
62+
pub(crate) fn depth(self) -> usize {
63+
self.depth
64+
}
65+
}
66+
5367
#[derive(Clone, Debug, Default, Eq, PartialEq)]
5468
pub(crate) struct DelimiterFrameState {
5569
stack: Vec<DelimiterFrameKind>,
70+
close_generations_by_resulting_depth: Option<Vec<u64>>,
5671
}
5772

5873
impl DelimiterFrameState {
74+
pub(crate) fn with_boundary_tracking() -> Self {
75+
Self {
76+
stack: Vec::new(),
77+
close_generations_by_resulting_depth: Some(Vec::new()),
78+
}
79+
}
80+
5981
#[inline]
6082
pub(crate) fn depth(&self) -> usize {
6183
self.stack.len()
@@ -69,6 +91,36 @@ impl DelimiterFrameState {
6991
self.stack.last() == Some(&DelimiterFrameKind::Bracket)
7092
}
7193

94+
pub(crate) fn boundary(&self) -> DelimiterBoundary {
95+
let depth = self.depth();
96+
let close_generation_below = depth
97+
.checked_sub(1)
98+
.and_then(|resulting_depth| {
99+
self.close_generations_by_resulting_depth
100+
.as_ref()
101+
.and_then(|generations| generations.get(resulting_depth))
102+
})
103+
.copied()
104+
.unwrap_or(0);
105+
106+
DelimiterBoundary {
107+
depth,
108+
close_generation_below,
109+
}
110+
}
111+
112+
pub(crate) fn has_closed_below(&self, boundary: DelimiterBoundary) -> bool {
113+
let Some(resulting_depth) = boundary.depth.checked_sub(1) else {
114+
return false;
115+
};
116+
self.close_generations_by_resulting_depth
117+
.as_ref()
118+
.and_then(|generations| generations.get(resulting_depth))
119+
.copied()
120+
.unwrap_or(0)
121+
!= boundary.close_generation_below
122+
}
123+
72124
pub(crate) fn apply_token(&mut self, token: &SqlToken) {
73125
let SqlToken::Symbol(symbol) = token else {
74126
return;
@@ -118,6 +170,7 @@ impl DelimiterFrameState {
118170
.is_some_and(|top| top == close_kind)
119171
{
120172
let _ = self.stack.pop();
173+
self.record_successful_close();
121174
}
122175
}
123176
}
@@ -140,6 +193,7 @@ impl DelimiterFrameState {
140193
.is_some_and(|top| top.can_be_closed_by(close_kind))
141194
{
142195
let _ = self.stack.pop();
196+
self.record_successful_close();
143197
if self.stack.len() < baseline_depth {
144198
return true;
145199
}
@@ -148,6 +202,21 @@ impl DelimiterFrameState {
148202

149203
false
150204
}
205+
206+
fn record_successful_close(&mut self) {
207+
let Some(close_generations_by_resulting_depth) =
208+
self.close_generations_by_resulting_depth.as_mut()
209+
else {
210+
return;
211+
};
212+
let resulting_depth = self.stack.len();
213+
if close_generations_by_resulting_depth.len() <= resulting_depth {
214+
close_generations_by_resulting_depth.resize(resulting_depth.saturating_add(1), 0);
215+
}
216+
if let Some(generation) = close_generations_by_resulting_depth.get_mut(resulting_depth) {
217+
*generation = generation.wrapping_add(1);
218+
}
219+
}
151220
}
152221

153222
pub(crate) fn line_start_snapshot_before_token(
@@ -191,6 +260,7 @@ pub(crate) fn line_closes_delimiter_frame_below_snapshot_before_token(
191260
mod tests {
192261
use super::{
193262
line_closes_delimiter_frame_below_snapshot_before_token, line_start_snapshot_before_token,
263+
DelimiterFrameState,
194264
};
195265
use crate::ui::sql_editor::query_text::tokenize_sql;
196266
use crate::ui::sql_editor::SqlToken;
@@ -242,4 +312,32 @@ mod tests {
242312
&tokens, 0, comma_idx, &snapshot,
243313
));
244314
}
315+
316+
#[test]
317+
fn boundary_detects_close_even_when_the_same_depth_reopens() {
318+
let mut state = DelimiterFrameState::with_boundary_tracking();
319+
state.apply_token(&SqlToken::Symbol("(".to_string()));
320+
let boundary = state.boundary();
321+
322+
state.apply_token(&SqlToken::Symbol(")".to_string()));
323+
state.apply_token(&SqlToken::Symbol("(".to_string()));
324+
325+
assert_eq!(state.depth(), boundary.depth());
326+
assert!(state.has_closed_below(boundary));
327+
}
328+
329+
#[test]
330+
fn boundary_ignores_inner_and_mismatched_closes() {
331+
let mut state = DelimiterFrameState::with_boundary_tracking();
332+
state.apply_token(&SqlToken::Symbol("(".to_string()));
333+
let boundary = state.boundary();
334+
335+
state.apply_token(&SqlToken::Symbol("[".to_string()));
336+
state.apply_token(&SqlToken::Symbol(")".to_string()));
337+
assert!(!state.has_closed_below(boundary));
338+
339+
state.apply_token(&SqlToken::Symbol("]".to_string()));
340+
assert!(!state.has_closed_below(boundary));
341+
assert_eq!(state.depth(), boundary.depth());
342+
}
245343
}

0 commit comments

Comments
 (0)