use crate::Style;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SyntaxToken {
pub line: u32,
pub start_col: u32,
pub end_col: u32,
pub category: String,
}
#[derive(Debug, Clone)]
pub struct ConcealDecoration {
pub start_col: usize,
pub end_col: usize,
pub replacement: Option<String>,
pub style: Option<Style>,
}
#[derive(Debug, Clone)]
pub struct ConcealedLine {
pub text: String,
pub col_mapping: Vec<u16>,
pub styles: Vec<Option<Style>>,
}
impl ConcealedLine {
#[must_use]
pub fn identity(content: &str) -> Self {
let char_count = content.chars().count();
#[allow(clippy::cast_possible_truncation)]
let col_mapping: Vec<u16> = (0..=char_count).map(|i| i as u16).collect();
let styles = vec![None; char_count];
Self {
text: content.to_owned(),
col_mapping,
styles,
}
}
}
struct ConcealRegion {
start_col: usize,
end_col: usize,
replacement: Option<String>,
style: Option<Style>,
}
#[must_use]
pub fn apply_conceals(content: &str, decorations: &[ConcealDecoration]) -> ConcealedLine {
if decorations.is_empty() {
return ConcealedLine::identity(content);
}
let char_count = content.chars().count();
let mut regions: Vec<ConcealRegion> = decorations
.iter()
.map(|d| ConcealRegion {
start_col: d.start_col,
end_col: d.end_col.min(char_count),
replacement: d.replacement.clone(),
style: d.style.clone(),
})
.collect();
regions.sort_by_key(|r| r.start_col);
let char_byte_offsets: Vec<usize> = content
.char_indices()
.map(|(byte, _)| byte)
.chain(std::iter::once(content.len()))
.collect();
let char_to_byte = |col: usize| -> usize {
char_byte_offsets
.get(col.min(char_count))
.copied()
.unwrap_or(content.len())
};
let mut result_text = String::with_capacity(content.len());
let mut col_mapping = Vec::with_capacity(content.len() + 1);
let mut styles = Vec::with_capacity(content.len());
let mut source_char = 0;
for region in ®ions {
if region.start_col < source_char {
continue;
}
let unchanged_end = region.start_col.min(char_count);
if source_char < unchanged_end {
let start_byte = char_to_byte(source_char);
let end_byte = char_to_byte(unchanged_end);
let slice = &content[start_byte..end_byte];
for (i, _) in slice.chars().enumerate() {
#[allow(clippy::cast_possible_truncation)]
col_mapping.push((source_char + i) as u16);
styles.push(None);
}
result_text.push_str(slice);
}
if let Some(replacement) = ®ion.replacement {
#[allow(clippy::cast_possible_truncation)]
let start = region.start_col as u16;
for _ in replacement.chars() {
col_mapping.push(start);
styles.push(region.style.clone());
}
result_text.push_str(replacement);
}
source_char = region.end_col.min(char_count);
}
if source_char < char_count {
let start_byte = char_to_byte(source_char);
let slice = &content[start_byte..];
for (i, _) in slice.chars().enumerate() {
#[allow(clippy::cast_possible_truncation)]
col_mapping.push((source_char + i) as u16);
styles.push(None);
}
result_text.push_str(slice);
}
#[allow(clippy::cast_possible_truncation)]
col_mapping.push(char_count as u16);
ConcealedLine {
text: result_text,
col_mapping,
styles,
}
}
#[must_use]
pub fn source_to_display_col(concealed: &ConcealedLine, source_col: usize) -> usize {
for (display_col, &mapped_source) in concealed.col_mapping.iter().enumerate() {
if mapped_source as usize >= source_col {
return display_col;
}
}
concealed.text.len()
}
#[must_use]
#[allow(clippy::many_single_char_names)]
fn lerp_color(from: reovim_arch::Color, to: reovim_arch::Color, t: f32) -> reovim_arch::Color {
let (from_r, from_g, from_b) = color_to_rgb(from);
let (to_r, to_g, to_b) = color_to_rgb(to);
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let red = (f32::from(to_r) - f32::from(from_r)).mul_add(t, f32::from(from_r)) as u8;
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let green = (f32::from(to_g) - f32::from(from_g)).mul_add(t, f32::from(from_g)) as u8;
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let blue = (f32::from(to_b) - f32::from(from_b)).mul_add(t, f32::from(from_b)) as u8;
reovim_arch::Color::Rgb {
r: red,
g: green,
b: blue,
}
}
#[allow(clippy::match_same_arms)]
#[cfg_attr(coverage_nightly, coverage(off))]
fn color_to_rgb(color: reovim_arch::Color) -> (u8, u8, u8) {
match color {
reovim_arch::Color::Rgb { r, g, b } => (r, g, b),
reovim_arch::Color::Black => (0, 0, 0),
reovim_arch::Color::DarkGrey => (128, 128, 128),
reovim_arch::Color::Red => (255, 0, 0),
reovim_arch::Color::DarkRed => (139, 0, 0),
reovim_arch::Color::Green => (0, 255, 0),
reovim_arch::Color::DarkGreen => (0, 128, 0),
reovim_arch::Color::Yellow => (255, 255, 0),
reovim_arch::Color::DarkYellow => (128, 128, 0),
reovim_arch::Color::Blue => (0, 0, 255),
reovim_arch::Color::DarkBlue => (0, 0, 139),
reovim_arch::Color::Magenta => (255, 0, 255),
reovim_arch::Color::DarkMagenta => (128, 0, 128),
reovim_arch::Color::Cyan => (0, 255, 255),
reovim_arch::Color::DarkCyan => (0, 128, 128),
reovim_arch::Color::White => (255, 255, 255),
reovim_arch::Color::Grey => (192, 192, 192),
reovim_arch::Color::AnsiValue(n) => ansi256_to_rgb(n),
reovim_arch::Color::Reset => (0, 0, 0),
}
}
#[allow(clippy::cast_possible_truncation)]
fn ansi256_to_rgb(n: u8) -> (u8, u8, u8) {
match n {
0..=15 => {
let table: [(u8, u8, u8); 16] = [
(0, 0, 0), (128, 0, 0), (0, 128, 0), (128, 128, 0), (0, 0, 128), (128, 0, 128), (0, 128, 128), (192, 192, 192), (128, 128, 128), (255, 0, 0), (0, 255, 0), (255, 255, 0), (0, 0, 255), (255, 0, 255), (0, 255, 255), (255, 255, 255), ];
table[n as usize]
}
16..=231 => {
let idx = n - 16;
let r = idx / 36;
let g = (idx % 36) / 6;
let b = idx % 6;
let to_val = |c: u8| if c == 0 { 0 } else { 55 + 40 * c };
(to_val(r), to_val(g), to_val(b))
}
232..=255 => {
let v = 8 + 10 * (n - 232);
(v, v, v)
}
}
}
#[must_use]
pub fn dim_style(style: &Style, opacity: f32, default_bg: reovim_arch::Color) -> Style {
if (opacity - 1.0).abs() < f32::EPSILON {
return style.clone();
}
let blend_opt = |color: Option<reovim_arch::Color>| -> Option<reovim_arch::Color> {
color.map(|c| lerp_color(default_bg, c, opacity))
};
Style {
fg: blend_opt(style.fg),
bg: blend_opt(style.bg),
attributes: style.attributes,
}
}
#[cfg(test)]
#[path = "conceal_tests.rs"]
mod tests;