mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-06 19:45:23 +00:00
perf(preview): cap the number of concurrent preview tokio tasks in the background (#67)
* perf(preview): cap the number of concurrent preview tokio tasks in the background * perf(preview): check filesize asynchronously to avoid blocking the UI * ci(changelog): fix action permissions
This commit is contained in:
parent
dc36b2152d
commit
84d54b5751
3
.github/workflows/changelog.yml
vendored
3
.github/workflows/changelog.yml
vendored
@ -1,9 +1,10 @@
|
|||||||
name: changelog
|
name: changelog
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
pull_request: write
|
||||||
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '[v]?[0-9]+.[0-9]+.[0-9]+'
|
- '[v]?[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
@ -35,7 +35,7 @@ pub enum PreviewContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const PREVIEW_NOT_SUPPORTED_MSG: &str =
|
pub const PREVIEW_NOT_SUPPORTED_MSG: &str =
|
||||||
"Preview for this file type is not yet supported";
|
"Preview for this file type is not supported";
|
||||||
pub const FILE_TOO_LARGE_MSG: &str = "File too large";
|
pub const FILE_TOO_LARGE_MSG: &str = "File too large";
|
||||||
|
|
||||||
/// A preview of an entry.
|
/// A preview of an entry.
|
||||||
|
@ -5,6 +5,7 @@ use parking_lot::Mutex;
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, Seek};
|
use std::io::{BufRead, BufReader, Seek};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::atomic::{AtomicU8, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use syntect::{
|
use syntect::{
|
||||||
@ -28,6 +29,8 @@ pub struct FilePreviewer {
|
|||||||
cache: Arc<Mutex<PreviewCache>>,
|
cache: Arc<Mutex<PreviewCache>>,
|
||||||
pub syntax_set: Arc<SyntaxSet>,
|
pub syntax_set: Arc<SyntaxSet>,
|
||||||
pub syntax_theme: Arc<Theme>,
|
pub syntax_theme: Arc<Theme>,
|
||||||
|
concurrent_preview_tasks: Arc<AtomicU8>,
|
||||||
|
last_previewed: Arc<Mutex<Arc<Preview>>>,
|
||||||
//image_picker: Arc<Mutex<Picker>>,
|
//image_picker: Arc<Mutex<Picker>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +45,12 @@ impl FilePreviewerConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The maximum file size that we will try to preview.
|
||||||
|
/// 4 MB
|
||||||
|
const MAX_FILE_SIZE: u64 = 4 * 1024 * 1024;
|
||||||
|
|
||||||
|
const MAX_CONCURRENT_PREVIEW_TASKS: u8 = 2;
|
||||||
|
|
||||||
impl FilePreviewer {
|
impl FilePreviewer {
|
||||||
pub fn new(config: Option<FilePreviewerConfig>) -> Self {
|
pub fn new(config: Option<FilePreviewerConfig>) -> Self {
|
||||||
let hl_assets = load_highlighting_assets();
|
let hl_assets = load_highlighting_assets();
|
||||||
@ -62,14 +71,12 @@ impl FilePreviewer {
|
|||||||
cache: Arc::new(Mutex::new(PreviewCache::default())),
|
cache: Arc::new(Mutex::new(PreviewCache::default())),
|
||||||
syntax_set: Arc::new(syntax_set),
|
syntax_set: Arc::new(syntax_set),
|
||||||
syntax_theme: Arc::new(theme),
|
syntax_theme: Arc::new(theme),
|
||||||
|
concurrent_preview_tasks: Arc::new(AtomicU8::new(0)),
|
||||||
|
last_previewed: Arc::new(Mutex::new(Arc::new(Preview::default()))),
|
||||||
//image_picker: Arc::new(Mutex::new(image_picker)),
|
//image_picker: Arc::new(Mutex::new(image_picker)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum file size that we will try to preview.
|
|
||||||
/// 4 MB
|
|
||||||
const MAX_FILE_SIZE: u64 = 4 * 1024 * 1024;
|
|
||||||
|
|
||||||
/// Get a preview for a file entry.
|
/// Get a preview for a file entry.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -83,44 +90,32 @@ impl FilePreviewer {
|
|||||||
}
|
}
|
||||||
debug!("No preview in cache for {:?}", entry.name);
|
debug!("No preview in cache for {:?}", entry.name);
|
||||||
|
|
||||||
// check file size
|
if self.concurrent_preview_tasks.load(Ordering::Relaxed)
|
||||||
if get_file_size(&path_buf).map_or(false, |s| s > Self::MAX_FILE_SIZE)
|
< MAX_CONCURRENT_PREVIEW_TASKS
|
||||||
{
|
{
|
||||||
debug!("File too large: {:?}", entry.name);
|
self.concurrent_preview_tasks
|
||||||
let preview = meta::file_too_large(&entry.name);
|
.fetch_add(1, Ordering::Relaxed);
|
||||||
self.cache_preview(entry.name.clone(), preview.clone());
|
let cache = self.cache.clone();
|
||||||
return preview;
|
let entry_c = entry.clone();
|
||||||
|
let syntax_set = self.syntax_set.clone();
|
||||||
|
let syntax_theme = self.syntax_theme.clone();
|
||||||
|
let path = path_buf.clone();
|
||||||
|
let concurrent_tasks = self.concurrent_preview_tasks.clone();
|
||||||
|
let last_previewed = self.last_previewed.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
try_preview(
|
||||||
|
entry_c,
|
||||||
|
path,
|
||||||
|
cache,
|
||||||
|
syntax_set,
|
||||||
|
syntax_theme,
|
||||||
|
concurrent_tasks,
|
||||||
|
last_previewed,
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to determine file type
|
self.last_previewed.lock().clone()
|
||||||
debug!("Computing preview for {:?}", entry.name);
|
|
||||||
if let FileType::Text = FileType::from(&path_buf) {
|
|
||||||
debug!("File is text-based: {:?}", entry.name);
|
|
||||||
match File::open(&path_buf) {
|
|
||||||
Ok(file) => {
|
|
||||||
// insert a loading preview into the cache
|
|
||||||
let preview = meta::loading(&entry.name);
|
|
||||||
self.cache_preview(entry.name.clone(), preview.clone());
|
|
||||||
|
|
||||||
// compute the highlighted version in the background
|
|
||||||
let mut reader = BufReader::new(file);
|
|
||||||
reader.seek(std::io::SeekFrom::Start(0)).unwrap();
|
|
||||||
self.compute_highlighted_text_preview(entry, reader);
|
|
||||||
preview
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error opening file: {:?}", e);
|
|
||||||
let p = meta::not_supported(&entry.name);
|
|
||||||
self.cache_preview(entry.name.clone(), p.clone());
|
|
||||||
p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debug!("File isn't text-based: {:?}", entry.name);
|
|
||||||
let preview = meta::not_supported(&entry.name);
|
|
||||||
self.cache_preview(entry.name.clone(), preview.clone());
|
|
||||||
preview
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//async fn compute_image_preview(&self, entry: &entry::Entry) {
|
//async fn compute_image_preview(&self, entry: &entry::Entry) {
|
||||||
@ -145,19 +140,71 @@ impl FilePreviewer {
|
|||||||
// });
|
// });
|
||||||
//}
|
//}
|
||||||
|
|
||||||
fn compute_highlighted_text_preview(
|
#[allow(dead_code)]
|
||||||
&self,
|
fn cache_preview(&mut self, key: String, preview: Arc<Preview>) {
|
||||||
|
self.cache.lock().insert(key, preview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_preview(
|
||||||
|
entry: entry::Entry,
|
||||||
|
path: PathBuf,
|
||||||
|
cache: Arc<Mutex<PreviewCache>>,
|
||||||
|
syntax_set: Arc<SyntaxSet>,
|
||||||
|
syntax_theme: Arc<Theme>,
|
||||||
|
concurrent_tasks: Arc<AtomicU8>,
|
||||||
|
last_previewed: Arc<Mutex<Arc<Preview>>>,
|
||||||
|
) {
|
||||||
|
debug!("Computing preview for {:?}", entry.name);
|
||||||
|
tokio::spawn(async move {
|
||||||
|
// check file size
|
||||||
|
if get_file_size(&path).map_or(false, |s| s > MAX_FILE_SIZE) {
|
||||||
|
debug!("File too large: {:?}", entry.name);
|
||||||
|
let preview = meta::file_too_large(&entry.name);
|
||||||
|
cache.lock().insert(entry.name.clone(), preview.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(FileType::from(&path), FileType::Text) {
|
||||||
|
debug!("File is text-based: {:?}", entry.name);
|
||||||
|
match File::open(&path) {
|
||||||
|
Ok(file) => {
|
||||||
|
// compute the highlighted version in the background
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
reader.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||||
|
let preview = compute_highlighted_text_preview(
|
||||||
|
&entry,
|
||||||
|
reader,
|
||||||
|
&syntax_set,
|
||||||
|
&syntax_theme,
|
||||||
|
);
|
||||||
|
cache.lock().insert(entry.name.clone(), preview.clone());
|
||||||
|
let mut tp = last_previewed.lock();
|
||||||
|
*tp = preview;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error opening file: {:?}", e);
|
||||||
|
let p = meta::not_supported(&entry.name);
|
||||||
|
cache.lock().insert(entry.name.clone(), p.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("File isn't text-based: {:?}", entry.name);
|
||||||
|
let preview = meta::not_supported(&entry.name);
|
||||||
|
cache.lock().insert(entry.name.clone(), preview.clone());
|
||||||
|
}
|
||||||
|
concurrent_tasks.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_highlighted_text_preview(
|
||||||
entry: &entry::Entry,
|
entry: &entry::Entry,
|
||||||
reader: BufReader<File>,
|
reader: BufReader<File>,
|
||||||
) {
|
syntax_set: &SyntaxSet,
|
||||||
let cache = self.cache.clone();
|
syntax_theme: &Theme,
|
||||||
let syntax_set = self.syntax_set.clone();
|
) -> Arc<Preview> {
|
||||||
let syntax_theme = self.syntax_theme.clone();
|
|
||||||
let entry_c = entry.clone();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
debug!(
|
debug!(
|
||||||
"Computing highlights in the background for {:?}",
|
"Computing highlights in the background for {:?}",
|
||||||
entry_c.name
|
entry.name
|
||||||
);
|
);
|
||||||
let lines: Vec<String> = reader
|
let lines: Vec<String> = reader
|
||||||
.lines()
|
.lines()
|
||||||
@ -168,39 +215,23 @@ impl FilePreviewer {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
match syntax::compute_highlights_for_path(
|
match syntax::compute_highlights_for_path(
|
||||||
&PathBuf::from(&entry_c.name),
|
&PathBuf::from(&entry.name),
|
||||||
lines,
|
lines,
|
||||||
&syntax_set,
|
syntax_set,
|
||||||
&syntax_theme,
|
syntax_theme,
|
||||||
) {
|
) {
|
||||||
Ok(highlighted_lines) => {
|
Ok(highlighted_lines) => {
|
||||||
debug!(
|
debug!("Successfully computed highlights for {:?}", entry.name);
|
||||||
"Successfully computed highlights for {:?}",
|
|
||||||
entry_c.name
|
|
||||||
);
|
|
||||||
cache.lock().insert(
|
|
||||||
entry_c.name.clone(),
|
|
||||||
Arc::new(Preview::new(
|
Arc::new(Preview::new(
|
||||||
entry_c.name,
|
entry.name.clone(),
|
||||||
PreviewContent::SyntectHighlightedText(
|
PreviewContent::SyntectHighlightedText(highlighted_lines),
|
||||||
highlighted_lines,
|
entry.icon,
|
||||||
),
|
))
|
||||||
entry_c.icon,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
debug!("Inserted highlighted preview into cache");
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Error computing highlights: {:?}", e);
|
warn!("Error computing highlights: {:?}", e);
|
||||||
let preview = meta::not_supported(&entry_c.name);
|
meta::not_supported(&entry.name)
|
||||||
cache.lock().insert(entry_c.name.clone(), preview);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cache_preview(&mut self, key: String, preview: Arc<Preview>) {
|
|
||||||
self.cache.lock().insert(key, preview);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user