use crate::
{
buffer::TextBuffer,
terminal::TerminalOps,
error::Error,
};
use crossterm::style::{ Color, SetForegroundColor, ResetColor };
#[derive( Debug, Clone )]
pub struct RenderConfig
{
pub show_line_numbers: bool,
pub show_status: bool,
pub show_char_count: bool,
pub color: bool,
pub prompt: String,
pub placeholder: Option< String >,
}
impl Default for RenderConfig
{
fn default() -> Self
{
Self
{
show_line_numbers: false,
show_status: false,
show_char_count: false,
color: true,
prompt: String::new(),
placeholder: None,
}
}
}
const MIN_WIDTH: u16 = 20; const MIN_HEIGHT: u16 = 3;
pub fn render< T >(
terminal: &mut T,
buffer: &TextBuffer,
config: &RenderConfig,
) -> Result< (), Error >
where
T: TerminalOps,
{
let ( term_width, term_height ) = terminal.size()?;
if term_width < MIN_WIDTH || term_height < MIN_HEIGHT
{
return Err( Error::TerminalTooSmall
{
width: term_width,
height: term_height,
min_width: MIN_WIDTH,
min_height: MIN_HEIGHT,
} );
}
terminal.move_cursor( 0, 0 )?;
terminal.clear_screen()?;
let mut row = 0;
if !config.prompt.is_empty()
{
terminal.move_cursor( 0, row )?;
if config.color
{
terminal.write_str( &format!( "{}", SetForegroundColor( Color::Cyan ) ) )?;
}
terminal.write_str( &config.prompt )?;
if config.color
{
terminal.write_str( &format!( "{}", ResetColor ) )?;
}
row += 1;
}
let line_count = buffer.line_count();
let line_number_width = if config.show_line_numbers
{
format!( "{}", line_count ).len() + 2
}
else
{
0
};
for ( idx, line ) in buffer.lines().iter().enumerate()
{
if row >= term_height - 1
{
break; }
terminal.move_cursor( 0, row )?;
if config.show_line_numbers
{
if config.color
{
terminal.write_str( &format!( "{}", SetForegroundColor( Color::DarkGrey ) ) )?;
}
let line_num = format!( "{:>width$} ", idx + 1, width = line_number_width - 1 );
terminal.write_str( &line_num )?;
if config.color
{
terminal.write_str( &format!( "{}", ResetColor ) )?;
}
}
if line.is_empty() && buffer.line_count() == 1
{
if let Some( placeholder ) = &config.placeholder
{
if config.color
{
terminal.write_str( &format!( "{}", SetForegroundColor( Color::DarkGrey ) ) )?;
}
terminal.write_str( placeholder )?;
if config.color
{
terminal.write_str( &format!( "{}", ResetColor ) )?;
}
}
}
else
{
terminal.write_str( line )?;
}
row += 1;
}
if config.show_status && row < term_height
{
terminal.move_cursor( 0, row )?;
terminal.clear_line()?;
if config.color
{
terminal.write_str( &format!( "{}", SetForegroundColor( Color::DarkGrey ) ) )?;
}
let ( cursor_line, cursor_col ) = buffer.cursor_position();
let status = if config.show_char_count
{
format!(
"Line {}/{} Col {} | {} chars",
cursor_line + 1,
line_count,
cursor_col + 1,
buffer.char_count()
)
}
else
{
format!(
"Line {}/{} Col {}",
cursor_line + 1,
line_count,
cursor_col + 1
)
};
terminal.write_str( &status )?;
if config.color
{
terminal.write_str( &format!( "{}", ResetColor ) )?;
}
}
let ( cursor_line, cursor_col ) = buffer.cursor_position();
let cursor_row = if config.prompt.is_empty()
{
cursor_line as u16
}
else
{
cursor_line as u16 + 1
};
let cursor_col_offset = if config.show_line_numbers
{
line_number_width as u16
}
else
{
0
};
let current_line = buffer.current_line();
let display_col = current_line
.chars()
.take( cursor_col )
.map( |c| unicode_width::UnicodeWidthChar::width( c ).unwrap_or( 1 ) )
.sum::< usize >() as u16;
terminal.move_cursor( cursor_col_offset + display_col, cursor_row )?;
terminal.flush()?;
Ok( () )
}
pub fn render_result< T >(
terminal: &mut T,
text: &str,
config: &RenderConfig,
) -> std::io::Result< () >
where
T: TerminalOps,
{
terminal.move_cursor( 0, 0 )?;
terminal.clear_screen()?;
let mut row = 0;
if !config.prompt.is_empty()
{
terminal.move_cursor( 0, row )?;
if config.color
{
write!( terminal, "{}", SetForegroundColor( Color::Cyan ) )?;
}
terminal.write_str( &config.prompt )?;
if config.color
{
write!( terminal, "{}", ResetColor )?;
}
row += 1;
}
for line in text.lines()
{
terminal.move_cursor( 0, row )?;
terminal.write_str( line )?;
row += 1;
}
terminal.write_str( "\n" )?;
terminal.flush()?;
Ok( () )
}
pub fn render_cancelled< T >(
terminal: &mut T,
config: &RenderConfig,
) -> std::io::Result< () >
where
T: TerminalOps,
{
terminal.move_cursor( 0, 0 )?;
terminal.clear_screen()?;
if !config.prompt.is_empty()
{
if config.color
{
write!( terminal, "{}", SetForegroundColor( Color::Yellow ) )?;
}
terminal.write_str( "Cancelled\n" )?;
if config.color
{
write!( terminal, "{}", ResetColor )?;
}
}
terminal.flush()?;
Ok( () )
}