use ratatui::style::Style;
use ratatui::text::Span;
use typ_core::ThemeColors;
fn digits(line_count: usize) -> usize {
let mut n = line_count.max(1);
let mut digits = 1;
while n >= 10 {
n /= 10;
digits += 1;
}
digits
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GutterComponent {
LineNumbers { relative: bool },
Spacer,
Diagnostics,
Diff,
}
impl GutterComponent {
pub fn width(&self, line_count: usize) -> usize {
match self {
GutterComponent::LineNumbers { .. } => digits(line_count),
GutterComponent::Spacer | GutterComponent::Diagnostics | GutterComponent::Diff => 1,
}
}
fn render_line(
&self,
line: usize,
cursor_line: usize,
line_count: usize,
theme: &ThemeColors,
) -> Span<'static> {
match self {
GutterComponent::LineNumbers { relative } => {
let number = if *relative && line != cursor_line {
cursor_line.abs_diff(line)
} else {
line + 1
};
let width = digits(line_count);
let style = if line == cursor_line {
Style::default().fg(theme.line_number_current_fg)
} else {
Style::default().fg(theme.line_number_fg)
};
Span::styled(format!("{number:>width$}"), style)
}
GutterComponent::Diagnostics | GutterComponent::Diff => {
Span::styled(" ", Style::default().fg(theme.gutter_fg))
}
GutterComponent::Spacer => Span::raw(" "),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Gutter {
components: Vec<GutterComponent>,
}
impl Default for Gutter {
fn default() -> Self {
Self::new(vec![
GutterComponent::LineNumbers { relative: false },
GutterComponent::Spacer,
])
}
}
impl Gutter {
pub fn new(components: Vec<GutterComponent>) -> Self {
Self { components }
}
pub fn width(&self, line_count: usize) -> usize {
self.components.iter().map(|c| c.width(line_count)).sum()
}
pub fn render_line(
&self,
line: usize,
cursor_line: usize,
line_count: usize,
theme: &ThemeColors,
) -> Vec<Span<'static>> {
self.components
.iter()
.map(|c| c.render_line(line, cursor_line, line_count, theme))
.collect()
}
}