perf(previews): avoid unnecessary preview content copy (#507)

This commit is contained in:
Alexandre Pasmantier 2025-05-14 21:25:25 +02:00 committed by GitHub
parent 67c067ff40
commit fc2f8b9473
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 50 deletions

View File

@ -2,12 +2,6 @@ use std::hash::{Hash, Hasher};
use devicons::FileIcon; use devicons::FileIcon;
// NOTE: having an enum for entry types would be nice since it would allow
// having a nicer implementation for transitions between channels. This would
// permit implementing `From<EntryType>` for channels which would make the
// channel convertible from any other that yields `EntryType`.
// This needs pondering since it does bring another level of abstraction and
// adds a layer of complexity.
#[derive(Clone, Debug, Eq)] #[derive(Clone, Debug, Eq)]
pub struct Entry { pub struct Entry {
/// The name of the entry. /// The name of the entry.

View File

@ -9,6 +9,8 @@ pub struct PreviewState {
} }
const PREVIEW_MIN_SCROLL_LINES: u16 = 3; const PREVIEW_MIN_SCROLL_LINES: u16 = 3;
pub const ANSI_BEFORE_CONTEXT_SIZE: u16 = 10;
const ANSI_CONTEXT_SIZE: usize = 500;
impl PreviewState { impl PreviewState {
pub fn new( pub fn new(
@ -55,4 +57,28 @@ impl PreviewState {
self.target_line = target_line; self.target_line = target_line;
} }
} }
pub fn for_render_context(&self) -> Self {
let skipped_lines =
self.scroll.saturating_sub(ANSI_BEFORE_CONTEXT_SIZE);
let cropped_content = self
.preview
.content
.lines()
.skip(skipped_lines as usize)
.take(ANSI_CONTEXT_SIZE)
.collect::<Vec<_>>()
.join("\n");
PreviewState::new(
self.enabled,
Preview::new(
&self.preview.title,
cropped_content,
self.preview.icon,
self.preview.total_lines,
),
skipped_lines,
self.target_line,
)
}
} }

View File

@ -32,21 +32,13 @@ pub fn draw_preview_content_block(
use_nerd_font_icons, use_nerd_font_icons,
)?; )?;
// render the preview content // render the preview content
let rp = build_preview_paragraph( let rp = build_preview_paragraph(&preview_state.preview.content);
&preview_state.preview.content,
preview_state.target_line,
preview_state.scroll,
);
f.render_widget(rp, inner); f.render_widget(rp, inner);
Ok(()) Ok(())
} }
pub fn build_preview_paragraph( pub fn build_preview_paragraph(content: &str) -> Paragraph<'_> {
preview_content: &str,
#[allow(unused_variables)] target_line: Option<u16>,
preview_scroll: u16,
) -> Paragraph<'_> {
let preview_block = let preview_block =
Block::default().style(Style::default()).padding(Padding { Block::default().style(Style::default()).padding(Padding {
top: 0, top: 0,
@ -55,42 +47,14 @@ pub fn build_preview_paragraph(
left: 1, left: 1,
}); });
build_ansi_text_paragraph(preview_content, preview_block, preview_scroll) build_ansi_text_paragraph(content, preview_block)
} }
const ANSI_BEFORE_CONTEXT_SIZE: u16 = 10;
const ANSI_CONTEXT_SIZE: usize = 150;
fn build_ansi_text_paragraph<'a>( fn build_ansi_text_paragraph<'a>(
text: &'a str, text: &'a str,
preview_block: Block<'a>, preview_block: Block<'a>,
preview_scroll: u16,
) -> Paragraph<'a> { ) -> Paragraph<'a> {
let lines = text.lines(); Paragraph::new(text.into_text().unwrap()).block(preview_block)
let skip =
preview_scroll.saturating_sub(ANSI_BEFORE_CONTEXT_SIZE) as usize;
let context = lines
.skip(skip)
.take(ANSI_CONTEXT_SIZE)
.collect::<Vec<_>>()
.join("\n");
let mut text = "\n".repeat(skip);
text.push_str(
&replace_non_printable(
context.as_bytes(),
&ReplaceNonPrintableConfig {
replace_line_feed: false,
replace_control_characters: false,
..Default::default()
},
)
.0,
);
Paragraph::new(text.into_text().unwrap())
.block(preview_block)
.scroll((preview_scroll, 0))
} }
pub fn build_meta_preview_paragraph<'a>( pub fn build_meta_preview_paragraph<'a>(

View File

@ -187,9 +187,7 @@ impl Television {
self.rc_picker.clone(), self.rc_picker.clone(),
channel_state, channel_state,
self.spinner, self.spinner,
// PERF: we shouldn't need to clone the whole preview here but only self.preview_state.for_render_context(),
// what's in range of the preview window
self.preview_state.clone(),
); );
Ctx::new( Ctx::new(
@ -197,7 +195,6 @@ impl Television {
self.config.clone(), self.config.clone(),
self.colorscheme.clone(), self.colorscheme.clone(),
self.app_metadata.clone(), self.app_metadata.clone(),
// now timestamp
std::time::Instant::now(), std::time::Instant::now(),
self.ui_state.layout, self.ui_state.layout,
) )