style: format the whole tree with cargo fmt (stock defaults, #35)
One-time, mechanical reformat — no functional changes. The tree was not rustfmt-clean (~1800 hunks across ~100 files); this brings it to stock `cargo fmt` defaults so a `cargo fmt --check` CI gate can follow. Behaviour-preserving: 2509 pass / 0 fail / 1 ignored (unchanged baseline), clippy clean. A .git-blame-ignore-revs entry follows so `git blame` skips this commit.
This commit is contained in:
@@ -196,7 +196,16 @@ fn render_badge_box(label: &str, area: Rect, above: Option<Rect>, frame: &mut Fr
|
||||
area.y + area.height - box_h - 1
|
||||
}
|
||||
};
|
||||
fill_overlay_rect(Rect { x, y, width: box_w, height: box_h }, label.to_string(), frame);
|
||||
fill_overlay_rect(
|
||||
Rect {
|
||||
x,
|
||||
y,
|
||||
width: box_w,
|
||||
height: box_h,
|
||||
},
|
||||
label.to_string(),
|
||||
frame,
|
||||
);
|
||||
}
|
||||
|
||||
/// A step-caption box inset one cell from the bottom-right of `area`
|
||||
@@ -309,7 +318,9 @@ fn render_path_entry(
|
||||
let inner_w = dialog_w.saturating_sub(4) as usize;
|
||||
let prompt_lines = wrap_lines(&m.prompt, inner_w);
|
||||
// Title + blank + prompt + blank + input box (1 row + borders) + blank + key hints.
|
||||
let dialog_h = (prompt_lines.len() as u16).saturating_add(8).min(area.height);
|
||||
let dialog_h = (prompt_lines.len() as u16)
|
||||
.saturating_add(8)
|
||||
.min(area.height);
|
||||
let x = area.x + (area.width.saturating_sub(dialog_w)) / 2;
|
||||
let y = area.y + (area.height.saturating_sub(dialog_h)) / 2;
|
||||
let dialog_area = Rect {
|
||||
@@ -320,9 +331,7 @@ fn render_path_entry(
|
||||
};
|
||||
frame.render_widget(ratatui::widgets::Clear, dialog_area);
|
||||
|
||||
let title_style = Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let title_style = Style::default().fg(theme.fg).add_modifier(Modifier::BOLD);
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
@@ -386,9 +395,7 @@ fn render_load_picker(
|
||||
};
|
||||
frame.render_widget(ratatui::widgets::Clear, dialog_area);
|
||||
|
||||
let title_style = Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let title_style = Style::default().fg(theme.fg).add_modifier(Modifier::BOLD);
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
@@ -411,9 +418,7 @@ fn render_load_picker(
|
||||
let marker = if i == m.selected { "›" } else { " " };
|
||||
let temp_tag = if entry.is_temp { "[TEMP] " } else { "" };
|
||||
let style = if i == m.selected {
|
||||
Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default().fg(theme.fg)
|
||||
};
|
||||
@@ -447,11 +452,7 @@ fn render_load_picker(
|
||||
let display_input = if *cursor == input.len() {
|
||||
format!("{input}{cursor_marker}")
|
||||
} else {
|
||||
format!(
|
||||
"{}{cursor_marker}{}",
|
||||
&input[..*cursor],
|
||||
&input[*cursor..]
|
||||
)
|
||||
format!("{}{cursor_marker}{}", &input[..*cursor], &input[*cursor..])
|
||||
};
|
||||
text_lines.push(Line::from(format!("> {display_input}")));
|
||||
text_lines.push(Line::from(""));
|
||||
@@ -500,9 +501,7 @@ fn render_rebuild_confirm(summary: &str, theme: &Theme, frame: &mut Frame<'_>, a
|
||||
let bg = ratatui::widgets::Clear;
|
||||
frame.render_widget(bg, dialog_area);
|
||||
|
||||
let title_style = Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let title_style = Style::default().fg(theme.fg).add_modifier(Modifier::BOLD);
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
@@ -524,16 +523,12 @@ fn render_rebuild_confirm(summary: &str, theme: &Theme, frame: &mut Frame<'_>, a
|
||||
text_lines.push(Line::from(vec![
|
||||
Span::styled(
|
||||
"[Y]",
|
||||
Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw(format!(" {} ", crate::t!("shortcut.yes"))),
|
||||
Span::styled(
|
||||
"[N]",
|
||||
Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw(format!(" {} ", crate::t!("shortcut.no"))),
|
||||
Span::styled("Esc", Style::default().fg(theme.muted)),
|
||||
@@ -578,18 +573,14 @@ where
|
||||
/// dialog (issue #13): wide enough to hold the longest content
|
||||
/// line on a single row, clamped to sane bounds and the available
|
||||
/// area so a short insert no longer wraps on roomy terminals.
|
||||
fn undo_dialog_width(
|
||||
content_widths: impl IntoIterator<Item = usize>,
|
||||
area_width: u16,
|
||||
) -> u16 {
|
||||
fn undo_dialog_width(content_widths: impl IntoIterator<Item = usize>, area_width: u16) -> u16 {
|
||||
/// Floor — comfortably fits the button row plus borders.
|
||||
const MIN: u16 = 34;
|
||||
/// Ceiling for outlier (ultra-wide) terminals.
|
||||
const MAX: u16 = 100;
|
||||
let widest = content_widths.into_iter().max().unwrap_or(0);
|
||||
// +4: left/right border (2) + one padding column each side (2).
|
||||
let preferred =
|
||||
u16::try_from(widest).unwrap_or(u16::MAX).saturating_add(4);
|
||||
let preferred = u16::try_from(widest).unwrap_or(u16::MAX).saturating_add(4);
|
||||
let upper = area_width.min(MAX);
|
||||
let lower = MIN.min(upper);
|
||||
preferred.clamp(lower, upper)
|
||||
@@ -617,8 +608,7 @@ fn render_undo_confirm(
|
||||
let intro_line = format!("{intro} {}", m.command);
|
||||
// Local-time, human-formatted snapshot stamp (issue #13).
|
||||
let when_display = format_snapshot_timestamp(&m.timestamp);
|
||||
let when_line =
|
||||
crate::t!("modal.undo_confirm_when", timestamp = when_display);
|
||||
let when_line = crate::t!("modal.undo_confirm_when", timestamp = when_display);
|
||||
let prompt = crate::t!("modal.undo_confirm_prompt");
|
||||
// Reconstruct the button row purely to measure its width — the
|
||||
// styled spans are built below. Keep this in sync with them.
|
||||
@@ -681,9 +671,15 @@ fn render_undo_confirm(
|
||||
text_lines.push(Line::from(prompt));
|
||||
text_lines.push(Line::from(""));
|
||||
text_lines.push(Line::from(vec![
|
||||
Span::styled("[Y]", Style::default().fg(theme.fg).add_modifier(Modifier::BOLD)),
|
||||
Span::styled(
|
||||
"[Y]",
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw(format!(" {} ", crate::t!("shortcut.yes"))),
|
||||
Span::styled("[N]", Style::default().fg(theme.fg).add_modifier(Modifier::BOLD)),
|
||||
Span::styled(
|
||||
"[N]",
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw(format!(" {} ", crate::t!("shortcut.no"))),
|
||||
Span::styled("Esc", Style::default().fg(theme.muted)),
|
||||
Span::styled(
|
||||
@@ -811,17 +807,13 @@ fn clamp_wrapped(text: &str, width: usize, max_rows: usize) -> Vec<String> {
|
||||
|
||||
fn render_project_label(app: &App, theme: &Theme, frame: &mut Frame<'_>, area: Rect) {
|
||||
let label_style = Style::default().fg(theme.muted);
|
||||
let value_style = Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let value_style = Style::default().fg(theme.fg).add_modifier(Modifier::BOLD);
|
||||
let bar_style = Style::default().bg(theme.bg).fg(theme.muted);
|
||||
|
||||
let no_project = crate::t!("status.no_project");
|
||||
let display = app.project_name.as_deref().unwrap_or(no_project.as_str());
|
||||
let mut spans: Vec<Span<'_>> = vec![Span::styled(
|
||||
crate::t!("status.project_label"),
|
||||
label_style,
|
||||
)];
|
||||
let mut spans: Vec<Span<'_>> =
|
||||
vec![Span::styled(crate::t!("status.project_label"), label_style)];
|
||||
if app.project_is_temp {
|
||||
spans.push(Span::styled(
|
||||
"[TEMP] ",
|
||||
@@ -875,9 +867,7 @@ fn render_items_panel(app: &mut App, theme: &Theme, frame: &mut Frame<'_>, area:
|
||||
))
|
||||
.title(Span::styled(
|
||||
format!(" {} ", crate::t!("panel.tables_title")),
|
||||
Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD),
|
||||
))
|
||||
.style(Style::default().bg(theme.bg).fg(theme.fg));
|
||||
|
||||
@@ -918,9 +908,7 @@ fn render_items_panel(app: &mut App, theme: &Theme, frame: &mut Frame<'_>, area:
|
||||
let mut lines: Vec<Line<'_>> = Vec::new();
|
||||
for name in &app.tables {
|
||||
let style = if name == highlight {
|
||||
Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default().fg(theme.fg)
|
||||
};
|
||||
@@ -937,7 +925,9 @@ fn render_items_panel(app: &mut App, theme: &Theme, frame: &mut Frame<'_>, area:
|
||||
}
|
||||
}
|
||||
}
|
||||
let paragraph = Paragraph::new(lines).block(block).scroll((offset as u16, 0));
|
||||
let paragraph = Paragraph::new(lines)
|
||||
.block(block)
|
||||
.scroll((offset as u16, 0));
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
|
||||
@@ -956,9 +946,7 @@ fn render_relationships_panel(app: &mut App, theme: &Theme, frame: &mut Frame<'_
|
||||
))
|
||||
.title(Span::styled(
|
||||
format!(" {} ", crate::t!("panel.relationships_title")),
|
||||
Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD),
|
||||
))
|
||||
.style(Style::default().bg(theme.bg).fg(theme.fg));
|
||||
|
||||
@@ -992,12 +980,24 @@ fn render_relationships_panel(app: &mut App, theme: &Theme, frame: &mut Frame<'_
|
||||
ellipsize(&rel.name, inner_w),
|
||||
name_style,
|
||||
)));
|
||||
let parent = format!(" {}.{} ->", rel.parent_table, rel.parent_columns.join(", "));
|
||||
lines.push(Line::from(Span::styled(ellipsize(&parent, inner_w), detail_style)));
|
||||
let parent = format!(
|
||||
" {}.{} ->",
|
||||
rel.parent_table,
|
||||
rel.parent_columns.join(", ")
|
||||
);
|
||||
lines.push(Line::from(Span::styled(
|
||||
ellipsize(&parent, inner_w),
|
||||
detail_style,
|
||||
)));
|
||||
let child = format!(" {}.{}", rel.child_table, rel.child_columns.join(", "));
|
||||
lines.push(Line::from(Span::styled(ellipsize(&child, inner_w), detail_style)));
|
||||
lines.push(Line::from(Span::styled(
|
||||
ellipsize(&child, inner_w),
|
||||
detail_style,
|
||||
)));
|
||||
}
|
||||
let paragraph = Paragraph::new(lines).block(block).scroll((offset as u16, 0));
|
||||
let paragraph = Paragraph::new(lines)
|
||||
.block(block)
|
||||
.scroll((offset as u16, 0));
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
|
||||
@@ -1022,9 +1022,7 @@ fn render_output_panel(app: &mut App, theme: &Theme, frame: &mut Frame<'_>, area
|
||||
.border_style(Style::default().fg(theme.border))
|
||||
.title(Span::styled(
|
||||
format!(" {} ", crate::t!("panel.output_title")),
|
||||
Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD),
|
||||
))
|
||||
.style(Style::default().bg(theme.bg).fg(theme.fg));
|
||||
|
||||
@@ -1132,9 +1130,9 @@ const fn output_span_style(class: OutputStyleClass, theme: &Theme) -> Style {
|
||||
OutputStyleClass::Neutral => Style::new().fg(theme.fg),
|
||||
OutputStyleClass::Efficient => Style::new().fg(theme.plan_efficient),
|
||||
OutputStyleClass::Expensive => Style::new().fg(theme.warning),
|
||||
OutputStyleClass::AutomaticIndex => Style::new()
|
||||
.fg(theme.warning)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
OutputStyleClass::AutomaticIndex => {
|
||||
Style::new().fg(theme.warning).add_modifier(Modifier::BOLD)
|
||||
}
|
||||
// ADR-0038 §4 / §6: de-emphasised text — the `Executing SQL:`
|
||||
// prefix and every category-3 prose line (caveat + the
|
||||
// existing `client_side.*` notes). `theme.muted` is the
|
||||
@@ -1239,9 +1237,7 @@ fn render_output_line<'a>(line: &'a OutputLine, theme: &Theme) -> Line<'a> {
|
||||
&line.text[..prefix_len],
|
||||
Style::default().fg(theme.muted),
|
||||
));
|
||||
for run in
|
||||
crate::input_render::lex_to_runs_in_mode(rest, theme, Mode::Advanced)
|
||||
{
|
||||
for run in crate::input_render::lex_to_runs_in_mode(rest, theme, Mode::Advanced) {
|
||||
spans.push(Span::styled(
|
||||
&rest[run.byte_range.0..run.byte_range.1],
|
||||
run.style,
|
||||
@@ -1350,9 +1346,7 @@ fn render_input_panel(app: &mut App, theme: &Theme, frame: &mut Frame<'_>, area:
|
||||
Span::raw(" "),
|
||||
Span::styled(
|
||||
label,
|
||||
Style::default()
|
||||
.fg(mode_color)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
Style::default().fg(mode_color).add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::raw(" "),
|
||||
]);
|
||||
@@ -1474,7 +1468,10 @@ fn render_input_one_row(
|
||||
if offset > 0 {
|
||||
frame.render_widget(
|
||||
Paragraph::new(Span::styled("<", marker)),
|
||||
Rect { width: 1, ..text_area },
|
||||
Rect {
|
||||
width: 1,
|
||||
..text_area
|
||||
},
|
||||
);
|
||||
}
|
||||
if offset + eff < line_cols {
|
||||
@@ -1536,8 +1533,16 @@ fn render_input_two_rows(
|
||||
// Overflowing both rows reserves a marker column on each row's
|
||||
// outer edge; otherwise both rows use their full text width.
|
||||
let overflow = line_cols >= capacity;
|
||||
let row0_text_w = if overflow { row0_w.saturating_sub(1) } else { row0_w };
|
||||
let row1_text_w = if overflow { row1_w.saturating_sub(1) } else { row1_w };
|
||||
let row0_text_w = if overflow {
|
||||
row0_w.saturating_sub(1)
|
||||
} else {
|
||||
row0_w
|
||||
};
|
||||
let row1_text_w = if overflow {
|
||||
row1_w.saturating_sub(1)
|
||||
} else {
|
||||
row1_w
|
||||
};
|
||||
let eff_cap = row0_text_w + row1_text_w;
|
||||
|
||||
let start = offset.min(len);
|
||||
@@ -1552,7 +1557,11 @@ fn render_input_two_rows(
|
||||
)
|
||||
};
|
||||
|
||||
let row0_x = if overflow { text_area.x + 1 } else { text_area.x };
|
||||
let row0_x = if overflow {
|
||||
text_area.x + 1
|
||||
} else {
|
||||
text_area.x
|
||||
};
|
||||
frame.render_widget(
|
||||
Paragraph::new(to_line(&window[..split])),
|
||||
Rect {
|
||||
@@ -1622,10 +1631,7 @@ fn expand_runs_to_cells(
|
||||
/// Convert `StyledRun`s into ratatui `Span`s borrowed from
|
||||
/// `input`. The end-of-input cursor sentinel (empty range) is
|
||||
/// rendered as an inverted space.
|
||||
fn runs_to_spans<'a>(
|
||||
input: &'a str,
|
||||
runs: &[crate::input_render::StyledRun],
|
||||
) -> Vec<Span<'a>> {
|
||||
fn runs_to_spans<'a>(input: &'a str, runs: &[crate::input_render::StyledRun]) -> Vec<Span<'a>> {
|
||||
runs.iter()
|
||||
.map(|r| {
|
||||
if r.byte_range.0 == r.byte_range.1 {
|
||||
@@ -1710,21 +1716,14 @@ fn resolve_hint_lines(
|
||||
}
|
||||
}
|
||||
|
||||
fn render_hint_panel(
|
||||
theme: &Theme,
|
||||
frame: &mut Frame<'_>,
|
||||
area: Rect,
|
||||
lines: Vec<Line<'static>>,
|
||||
) {
|
||||
fn render_hint_panel(theme: &Theme, frame: &mut Frame<'_>, area: Rect, lines: Vec<Line<'static>>) {
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.border_style(Style::default().fg(theme.border))
|
||||
.title(Span::styled(
|
||||
format!(" {} ", crate::t!("panel.hint_title")),
|
||||
Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
Style::default().fg(theme.fg).add_modifier(Modifier::BOLD),
|
||||
))
|
||||
.style(Style::default().bg(theme.bg).fg(theme.fg));
|
||||
|
||||
@@ -1918,9 +1917,7 @@ fn status_bar_bindings(app: &App) -> Vec<(&'static str, String)> {
|
||||
}
|
||||
|
||||
fn render_status_bar(app: &App, theme: &Theme, frame: &mut Frame<'_>, area: Rect) {
|
||||
let key_style = Style::default()
|
||||
.fg(theme.fg)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let key_style = Style::default().fg(theme.fg).add_modifier(Modifier::BOLD);
|
||||
let sep_style = Style::default().fg(theme.muted);
|
||||
let label_style = Style::default().fg(theme.muted);
|
||||
let bar_style = Style::default().bg(theme.bg).fg(theme.muted);
|
||||
@@ -2016,9 +2013,16 @@ mod tests {
|
||||
};
|
||||
let rendered = render_output_line(&line, &theme);
|
||||
// [system] tag, then the dim prefix, then ≥1 SQL spans.
|
||||
assert!(rendered.spans.len() >= 3, "tag + prefix + sql: {:?}", rendered.spans);
|
||||
assert!(
|
||||
rendered.spans.len() >= 3,
|
||||
"tag + prefix + sql: {:?}",
|
||||
rendered.spans
|
||||
);
|
||||
assert_eq!(rendered.spans[0].content.as_ref(), "[system] ");
|
||||
assert_eq!(rendered.spans[1].content.as_ref(), crate::echo::TEACHING_ECHO_LABEL);
|
||||
assert_eq!(
|
||||
rendered.spans[1].content.as_ref(),
|
||||
crate::echo::TEACHING_ECHO_LABEL
|
||||
);
|
||||
assert_eq!(
|
||||
rendered.spans[1].style.fg,
|
||||
Some(theme.muted),
|
||||
@@ -2152,17 +2156,41 @@ mod tests {
|
||||
use crate::completion::{Candidate, CandidateKind, ModeClass};
|
||||
let theme = Theme::dark();
|
||||
let items = vec![
|
||||
Candidate { text: "table".into(), kind: CandidateKind::Keyword, mode: ModeClass::Both },
|
||||
Candidate { text: "index".into(), kind: CandidateKind::Keyword, mode: ModeClass::Advanced },
|
||||
Candidate { text: "relationship".into(), kind: CandidateKind::Keyword, mode: ModeClass::Simple },
|
||||
Candidate {
|
||||
text: "table".into(),
|
||||
kind: CandidateKind::Keyword,
|
||||
mode: ModeClass::Both,
|
||||
},
|
||||
Candidate {
|
||||
text: "index".into(),
|
||||
kind: CandidateKind::Keyword,
|
||||
mode: ModeClass::Advanced,
|
||||
},
|
||||
Candidate {
|
||||
text: "relationship".into(),
|
||||
kind: CandidateKind::Keyword,
|
||||
mode: ModeClass::Simple,
|
||||
},
|
||||
];
|
||||
let line = render_candidate_line(&items, None, 100, &theme);
|
||||
assert_eq!(line.spans[0].content.as_ref(), "table");
|
||||
assert_eq!(line.spans[0].style.fg, Some(theme.tok_keyword), "Both keeps the kind colour");
|
||||
assert_eq!(
|
||||
line.spans[0].style.fg,
|
||||
Some(theme.tok_keyword),
|
||||
"Both keeps the kind colour"
|
||||
);
|
||||
assert_eq!(line.spans[2].content.as_ref(), "index");
|
||||
assert_eq!(line.spans[2].style.fg, Some(theme.mode_advanced), "Advanced → advanced colour");
|
||||
assert_eq!(
|
||||
line.spans[2].style.fg,
|
||||
Some(theme.mode_advanced),
|
||||
"Advanced → advanced colour"
|
||||
);
|
||||
assert_eq!(line.spans[4].content.as_ref(), "relationship");
|
||||
assert_eq!(line.spans[4].style.fg, Some(theme.mode_simple), "Simple → simple colour");
|
||||
assert_eq!(
|
||||
line.spans[4].style.fg,
|
||||
Some(theme.mode_simple),
|
||||
"Simple → simple colour"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2173,8 +2201,16 @@ mod tests {
|
||||
use crate::completion::{Candidate, CandidateKind, ModeClass};
|
||||
let theme = Theme::dark();
|
||||
let items = vec![
|
||||
Candidate { text: "values".into(), kind: CandidateKind::Keyword, mode: ModeClass::Advanced },
|
||||
Candidate { text: "select".into(), kind: CandidateKind::Keyword, mode: ModeClass::Advanced },
|
||||
Candidate {
|
||||
text: "values".into(),
|
||||
kind: CandidateKind::Keyword,
|
||||
mode: ModeClass::Advanced,
|
||||
},
|
||||
Candidate {
|
||||
text: "select".into(),
|
||||
kind: CandidateKind::Keyword,
|
||||
mode: ModeClass::Advanced,
|
||||
},
|
||||
];
|
||||
let line = render_candidate_line(&items, None, 100, &theme);
|
||||
assert_eq!(
|
||||
@@ -2248,7 +2284,10 @@ mod tests {
|
||||
"the error body is neutral fg, not flooded red",
|
||||
);
|
||||
assert!(
|
||||
rendered.spans[1].style.add_modifier.contains(Modifier::BOLD),
|
||||
rendered.spans[1]
|
||||
.style
|
||||
.add_modifier
|
||||
.contains(Modifier::BOLD),
|
||||
"the error body is bold for weight without the red-wall readability cost",
|
||||
);
|
||||
}
|
||||
@@ -2509,10 +2548,14 @@ mod tests {
|
||||
"the tail around the cursor must be visible:\n{out}"
|
||||
);
|
||||
assert!(
|
||||
!out.lines().any(|l| l.contains("select * from Customers where")),
|
||||
!out.lines()
|
||||
.any(|l| l.contains("select * from Customers where")),
|
||||
"the head must be scrolled off:\n{out}"
|
||||
);
|
||||
assert!(out.contains('<'), "a left scroll marker signals the hidden head:\n{out}");
|
||||
assert!(
|
||||
out.contains('<'),
|
||||
"a left scroll marker signals the hidden head:\n{out}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2525,9 +2568,18 @@ mod tests {
|
||||
let theme = Theme::dark();
|
||||
// Narrow (sidebar hidden, DB1) so the line overflows the field.
|
||||
let out = render_to_string(&mut app, &theme, 60, 24);
|
||||
assert!(out.contains("select * from"), "head visible at Home:\n{out}");
|
||||
assert!(out.contains('>'), "a right scroll marker signals the hidden tail:\n{out}");
|
||||
assert!(!out.contains("Wonderland"), "the tail must be scrolled off:\n{out}");
|
||||
assert!(
|
||||
out.contains("select * from"),
|
||||
"head visible at Home:\n{out}"
|
||||
);
|
||||
assert!(
|
||||
out.contains('>'),
|
||||
"a right scroll marker signals the hidden tail:\n{out}"
|
||||
);
|
||||
assert!(
|
||||
!out.contains("Wonderland"),
|
||||
"the tail must be scrolled off:\n{out}"
|
||||
);
|
||||
}
|
||||
|
||||
// ---- ADR-0046 DA4: two-row input on tall terminals -----------
|
||||
@@ -2569,8 +2621,14 @@ mod tests {
|
||||
let theme = Theme::dark();
|
||||
// Very narrow + tall: two rows, but the line exceeds both.
|
||||
let out = render_to_string(&mut app, &theme, 38, 44);
|
||||
assert!(out.contains("Wonderland"), "the tail/cursor stays visible:\n{out}");
|
||||
assert!(out.contains('<'), "a left marker signals the hidden head:\n{out}");
|
||||
assert!(
|
||||
out.contains("Wonderland"),
|
||||
"the tail/cursor stays visible:\n{out}"
|
||||
);
|
||||
assert!(
|
||||
out.contains('<'),
|
||||
"a left marker signals the hidden head:\n{out}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2644,7 +2702,10 @@ mod tests {
|
||||
|
||||
/// The `key` column of the strip's bindings, in order.
|
||||
fn strip_keys(app: &App) -> Vec<&'static str> {
|
||||
status_bar_bindings(app).into_iter().map(|(k, _)| k).collect()
|
||||
status_bar_bindings(app)
|
||||
.into_iter()
|
||||
.map(|(k, _)| k)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The full rendered strip text (keys + labels + separators).
|
||||
@@ -2659,7 +2720,12 @@ mod tests {
|
||||
fn hint_text(lines: &[Line<'_>]) -> String {
|
||||
lines
|
||||
.iter()
|
||||
.map(|l| l.spans.iter().map(|s| s.content.clone()).collect::<String>())
|
||||
.map(|l| {
|
||||
l.spans
|
||||
.iter()
|
||||
.map(|s| s.content.clone())
|
||||
.collect::<String>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
@@ -2686,10 +2752,7 @@ mod tests {
|
||||
fn strip_sidebar_focus_state_is_pane_scroll_input() {
|
||||
let mut app = App::new();
|
||||
app.nav_focus = NavFocus::SidebarTables;
|
||||
assert_eq!(
|
||||
strip_keys(&app),
|
||||
vec!["Ctrl-O", "↑↓/PgUp/PgDn", "Esc"],
|
||||
);
|
||||
assert_eq!(strip_keys(&app), vec!["Ctrl-O", "↑↓/PgUp/PgDn", "Esc"],);
|
||||
// ...and the relationships sidebar is the same state.
|
||||
app.nav_focus = NavFocus::SidebarRelationships;
|
||||
assert_eq!(strip_keys(&app), vec!["Ctrl-O", "↑↓/PgUp/PgDn", "Esc"]);
|
||||
@@ -2802,11 +2865,7 @@ mod tests {
|
||||
assert_eq!(two, vec!["alpha beta", "gamma delta"]);
|
||||
// > max rows: clamp to max, last row ends with an ellipsis,
|
||||
// and every row stays within the width.
|
||||
let many = clamp_wrapped(
|
||||
"alpha beta gamma delta epsilon zeta eta theta iota",
|
||||
11,
|
||||
3,
|
||||
);
|
||||
let many = clamp_wrapped("alpha beta gamma delta epsilon zeta eta theta iota", 11, 3);
|
||||
assert_eq!(many.len(), 3);
|
||||
assert!(many[2].ends_with('…'), "last row ellipsized: {many:?}");
|
||||
for row in &many {
|
||||
@@ -2931,9 +2990,18 @@ mod tests {
|
||||
app.output.push_back(err);
|
||||
|
||||
let out = render_to_string(&mut app, &Theme::dark(), 100, 20);
|
||||
assert!(out.contains("running: drop table Orders"), "pending keeps running::\n{out}");
|
||||
assert!(out.contains("create table T with pk ✓"), "ok shows ✓:\n{out}");
|
||||
assert!(out.contains("insert into T values (1) ✗"), "err shows ✗:\n{out}");
|
||||
assert!(
|
||||
out.contains("running: drop table Orders"),
|
||||
"pending keeps running::\n{out}"
|
||||
);
|
||||
assert!(
|
||||
out.contains("create table T with pk ✓"),
|
||||
"ok shows ✓:\n{out}"
|
||||
);
|
||||
assert!(
|
||||
out.contains("insert into T values (1) ✗"),
|
||||
"err shows ✗:\n{out}"
|
||||
);
|
||||
assert!(
|
||||
!out.contains("running: create table"),
|
||||
"a completed echo drops the running: prefix:\n{out}"
|
||||
@@ -2970,7 +3038,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn format_snapshot_timestamp_falls_back_on_garbage() {
|
||||
assert_eq!(format_snapshot_timestamp("not a timestamp"), "not a timestamp");
|
||||
assert_eq!(
|
||||
format_snapshot_timestamp("not a timestamp"),
|
||||
"not a timestamp"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2999,9 +3070,9 @@ mod tests {
|
||||
let theme = Theme::dark();
|
||||
let out = render_to_string(&mut app, &theme, 120, 30);
|
||||
assert!(
|
||||
out.lines().any(|l| l.contains(
|
||||
"This will undo: insert into Customers values (1, 'Oliver Sturm')"
|
||||
)),
|
||||
out.lines()
|
||||
.any(|l| l
|
||||
.contains("This will undo: insert into Customers values (1, 'Oliver Sturm')")),
|
||||
"command must sit on one row on a wide terminal:\n{out}"
|
||||
);
|
||||
}
|
||||
@@ -3017,7 +3088,10 @@ mod tests {
|
||||
}));
|
||||
let theme = Theme::dark();
|
||||
let out = render_to_string(&mut app, &theme, 120, 30);
|
||||
assert!(out.contains("Snapshot taken"), "capitalized Snapshot:\n{out}");
|
||||
assert!(
|
||||
out.contains("Snapshot taken"),
|
||||
"capitalized Snapshot:\n{out}"
|
||||
);
|
||||
assert!(out.contains("[Y] Yes"), "capitalized Yes:\n{out}");
|
||||
assert!(out.contains("[N] No"), "capitalized No:\n{out}");
|
||||
assert!(
|
||||
@@ -3113,8 +3187,14 @@ mod tests {
|
||||
app.schema_cache.table_indexes.insert(
|
||||
"Customers".to_string(),
|
||||
vec![
|
||||
IndexEntry { name: "idx_email".to_string(), unique: false },
|
||||
IndexEntry { name: "uidx_login".to_string(), unique: true },
|
||||
IndexEntry {
|
||||
name: "idx_email".to_string(),
|
||||
unique: false,
|
||||
},
|
||||
IndexEntry {
|
||||
name: "uidx_login".to_string(),
|
||||
unique: true,
|
||||
},
|
||||
],
|
||||
);
|
||||
let theme = Theme::dark();
|
||||
@@ -3123,7 +3203,10 @@ mod tests {
|
||||
assert!(out.contains("Customers"), "table listed:\n{out}");
|
||||
assert!(out.contains("Orders"), "table listed:\n{out}");
|
||||
assert!(out.contains("idx_email"), "index nested in panel:\n{out}");
|
||||
assert!(out.contains("uidx_login [unique]"), "unique index marked:\n{out}");
|
||||
assert!(
|
||||
out.contains("uidx_login [unique]"),
|
||||
"unique index marked:\n{out}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3143,10 +3226,19 @@ mod tests {
|
||||
app.tables = vec!["Customers".to_string()];
|
||||
let theme = Theme::dark();
|
||||
let narrow = render_to_string(&mut app, &theme, 80, 24);
|
||||
assert!(!narrow.contains("Tables"), "sidebar hidden at 80 wide:\n{narrow}");
|
||||
assert!(
|
||||
!narrow.contains("Tables"),
|
||||
"sidebar hidden at 80 wide:\n{narrow}"
|
||||
);
|
||||
let wide = render_to_string(&mut app, &theme, 110, 24);
|
||||
assert!(wide.contains("Tables"), "sidebar shown at 110 wide:\n{wide}");
|
||||
assert!(wide.contains("Customers"), "tables listed when shown:\n{wide}");
|
||||
assert!(
|
||||
wide.contains("Tables"),
|
||||
"sidebar shown at 110 wide:\n{wide}"
|
||||
);
|
||||
assert!(
|
||||
wide.contains("Customers"),
|
||||
"tables listed when shown:\n{wide}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3181,7 +3273,10 @@ mod tests {
|
||||
let theme = Theme::dark();
|
||||
let out = render_to_string(&mut app, &theme, 110, 24);
|
||||
assert!(out.contains("Relationships"), "panel title present:\n{out}");
|
||||
assert!(out.contains("Customers_Orders"), "relationship name:\n{out}");
|
||||
assert!(
|
||||
out.contains("Customers_Orders"),
|
||||
"relationship name:\n{out}"
|
||||
);
|
||||
assert!(
|
||||
out.lines().any(|l| l.contains("Customers.id ->")),
|
||||
"parent endpoint, broken at the arrow:\n{out}"
|
||||
@@ -3228,8 +3323,14 @@ mod tests {
|
||||
|
||||
app.nav_focus = NavFocus::SidebarTables;
|
||||
let focused = render_to_string(&mut app, &theme, 80, 24);
|
||||
assert!(focused.contains("Tables"), "sidebar revealed in nav mode:\n{focused}");
|
||||
assert!(focused.contains("Customers"), "tables in the overlay:\n{focused}");
|
||||
assert!(
|
||||
focused.contains("Tables"),
|
||||
"sidebar revealed in nav mode:\n{focused}"
|
||||
);
|
||||
assert!(
|
||||
focused.contains("Customers"),
|
||||
"tables in the overlay:\n{focused}"
|
||||
);
|
||||
assert!(
|
||||
focused.contains("Relationships"),
|
||||
"relationships panel in the overlay:\n{focused}"
|
||||
@@ -3365,7 +3466,9 @@ mod tests {
|
||||
}
|
||||
let backend = TestBackend::new(width, height);
|
||||
let mut terminal = Terminal::new(backend).expect("create terminal");
|
||||
terminal.draw(|f| render(app, theme, f)).expect("draw frame");
|
||||
terminal
|
||||
.draw(|f| render(app, theme, f))
|
||||
.expect("draw frame");
|
||||
terminal.backend().buffer().clone()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user