use crate::buf::text::Text;
use crate::buf::unicode::char_is_whitespace;
use crate::hl::ColorSchemeArc;
use crate::hl::Highlight;
use crate::prelude::*;
use crate::syntax::Syntax;
use crate::syntax::SyntaxCapturePoint;
use crate::syntax::SyntaxCaptureValue;
use crate::ui::canvas::Canvas;
use crate::ui::canvas::Cell;
use crate::ui::viewport::Viewport;
use crossterm::style::Attributes;
use crossterm::style::Color;
use smallvec::SmallVec;
pub fn draw(
viewport: &Viewport,
text: &Text,
syntax: &Option<Syntax>,
colorscheme: &Option<ColorSchemeArc>,
actual_shape: &U16Rect,
canvas: &mut Canvas,
) {
if actual_shape.size().is_zero() {
return;
}
let upos: U16Pos = actual_shape.min().into();
let height = actual_shape.height();
let width = actual_shape.width();
let mut row_idx = 0_u16;
let mut line_idx = viewport.start_line_idx();
let mut buflines = text.rope().lines_at(line_idx);
let mut last_colorscheme_hl: Option<Highlight> = None;
let mut last_hl_capture: Option<SyntaxCaptureValue> = None;
let set_bg = |cell: &mut Cell| {
if let Some(colorscheme) = colorscheme {
if cell.symbol().chars().any(char_is_whitespace) {
cell.set_fg(Color::Reset);
} else {
cell.set_fg(colorscheme.ui_text());
}
cell.set_bg(colorscheme.ui_background());
cell.set_attrs(Attributes::none());
}
};
while line_idx < viewport.end_line_idx() {
debug_assert!(row_idx < height);
let mut start_fills_count = 0_usize;
let mut end_fills_count = 0_usize;
let bline = buflines.next().unwrap();
let line_viewport = viewport.lines().get(&line_idx).unwrap();
let rows_viewport = line_viewport.rows();
if !rows_viewport.is_empty() {
let first_row = rows_viewport.first().unwrap();
let last_row = rows_viewport.last().unwrap();
let first_row_idx = *first_row.0;
let last_row_idx = *last_row.0;
for (r_idx, r_viewport) in rows_viewport.iter() {
debug_assert_eq!(*r_idx, row_idx);
debug_assert!(row_idx < height);
let mut col_idx = 0_u16;
let start_fills = if row_idx == first_row_idx
&& line_viewport.start_filled_cols() > 0
{
start_fills_count += 1;
debug_assert_eq!(start_fills_count, 1);
line_viewport.start_filled_cols() as u16
} else {
0_u16
};
let end_fills =
if row_idx == last_row_idx && line_viewport.end_filled_cols() > 0 {
end_fills_count += 1;
debug_assert_eq!(end_fills_count, 1);
line_viewport.end_filled_cols() as u16
} else {
0_u16
};
if start_fills > 0 {
let mut cell = Cell::from('>');
set_bg(&mut cell);
let cells_upos = point!(col_idx + upos.x(), row_idx + upos.y());
canvas.frame_mut().set_n_cells_at(
cells_upos,
cell,
start_fills as usize,
);
col_idx += start_fills;
}
if r_viewport.end_char_idx() > r_viewport.start_char_idx() {
let mut char_idx = r_viewport.start_char_idx();
let mut chars_iter =
bline.get_chars_at(r_viewport.start_char_idx()).unwrap();
while char_idx < r_viewport.end_char_idx() {
let c = chars_iter.next().unwrap();
let (unicode_symbol, unicode_width) = text.char_symbol_and_width(c);
if unicode_width > 0 {
let cap_point = SyntaxCapturePoint { line_idx, char_idx };
if let Some(colorscheme) = colorscheme
&& let Some(syntax) = syntax
&& let Some(syn_highlight_capture) = syntax.highlight_capture()
&& syn_highlight_capture
.as_ref()
.nodes()
.contains_key(&cap_point)
{
let hl_caps = syn_highlight_capture
.as_ref()
.nodes()
.get(&cap_point)
.unwrap();
for hl_cap in hl_caps.values.iter() {
if let Some(hl) = colorscheme.highlights().get(&hl_cap.name) {
let fg = colorscheme.resolve_fg(&hl.fg);
let bg = colorscheme.resolve_bg(&hl.bg);
last_colorscheme_hl = Some(Highlight {
fg: Some(fg),
bg: Some(bg),
attrs: hl.attrs,
});
last_hl_capture = Some(hl_cap.clone());
break;
}
}
}
let set_hl = |cell: &mut Cell| {
if let Some(colorscheme_hl) = last_colorscheme_hl
&& let Some(ref hl_capture) = last_hl_capture
&& cap_point >= hl_capture.range.start_point
&& cap_point < hl_capture.range.end_point
{
if cell.symbol().chars().any(char_is_whitespace) {
cell.set_fg(Color::Reset);
cell.set_attrs(Attributes::none());
} else {
cell.set_fg(colorscheme_hl.fg.unwrap());
cell.set_attrs(colorscheme_hl.attrs);
}
cell.set_bg(colorscheme_hl.bg.unwrap());
} else if let Some(colorscheme) = colorscheme {
if cell.symbol().chars().any(char_is_whitespace) {
cell.set_fg(Color::Reset);
} else {
cell.set_fg(colorscheme.ui_text());
}
cell.set_bg(colorscheme.ui_background());
cell.set_attrs(Attributes::none());
}
};
let cell_upos = point!(col_idx + upos.x(), row_idx + upos.y());
if unicode_width > 1 {
let mut cells: SmallVec<[Cell; 32]> =
SmallVec::with_capacity(unicode_width);
let mut cell = Cell::with_symbol(unicode_symbol);
set_hl(&mut cell);
cells.push(cell);
let mut cell = Cell::empty();
set_hl(&mut cell);
cells.resize(unicode_width, cell);
canvas.frame_mut().set_cells_at(cell_upos, &cells);
} else {
let mut cell = Cell::with_symbol(unicode_symbol);
set_hl(&mut cell);
canvas.frame_mut().set_cell(cell_upos, cell);
};
col_idx += unicode_width as u16;
}
char_idx += 1;
}
}
let end_dcol_idx =
text.width_before(line_idx, r_viewport.end_char_idx());
let start_dcol_idx =
text.width_before(line_idx, r_viewport.start_char_idx());
let occupied_length =
(end_dcol_idx - start_dcol_idx) as u16 + start_fills + end_fills;
if width > occupied_length {
let left_length = width - occupied_length;
let mut cell = Cell::from(' ');
set_bg(&mut cell);
let cells_upos = point!(col_idx + upos.x(), row_idx + upos.y());
canvas.frame_mut().set_n_cells_at(
cells_upos,
cell,
left_length as usize,
);
col_idx += left_length;
}
if end_fills > 0 {
let mut cell = Cell::from('<');
set_bg(&mut cell);
let cells_upos = point!(col_idx + upos.x(), row_idx + upos.y());
canvas.frame_mut().set_n_cells_at(
cells_upos,
cell,
end_fills as usize,
);
col_idx += end_fills;
}
debug_assert_eq!(width, col_idx);
row_idx += 1;
}
}
line_idx += 1;
}
while row_idx < height {
let mut cell = Cell::from(' ');
set_bg(&mut cell);
let cells_upos = point!(upos.x(), row_idx + upos.y());
canvas
.frame_mut()
.set_n_cells_at(cells_upos, cell, width as usize);
row_idx += 1;
}
}