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;
// 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)]
pub struct Entry {
/// The name of the entry.

View File

@ -9,6 +9,8 @@ pub struct PreviewState {
}
const PREVIEW_MIN_SCROLL_LINES: u16 = 3;
pub const ANSI_BEFORE_CONTEXT_SIZE: u16 = 10;
const ANSI_CONTEXT_SIZE: usize = 500;
impl PreviewState {
pub fn new(
@ -55,4 +57,28 @@ impl PreviewState {
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,
)?;
// render the preview content
let rp = build_preview_paragraph(
&preview_state.preview.content,
preview_state.target_line,
preview_state.scroll,
);
let rp = build_preview_paragraph(&preview_state.preview.content);
f.render_widget(rp, inner);
Ok(())
}
pub fn build_preview_paragraph(
preview_content: &str,
#[allow(unused_variables)] target_line: Option<u16>,
preview_scroll: u16,
) -> Paragraph<'_> {
pub fn build_preview_paragraph(content: &str) -> Paragraph<'_> {
let preview_block =
Block::default().style(Style::default()).padding(Padding {
top: 0,
@ -55,42 +47,14 @@ pub fn build_preview_paragraph(
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>(
text: &'a str,
preview_block: Block<'a>,
preview_scroll: u16,
) -> Paragraph<'a> {
let lines = text.lines();
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))
Paragraph::new(text.into_text().unwrap()).block(preview_block)
}
pub fn build_meta_preview_paragraph<'a>(

View File

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