Skip to content

Commit

Permalink
Introduce FileClass
Browse files Browse the repository at this point in the history
  • Loading branch information
liuchengxu committed Dec 27, 2024
1 parent 56a35db commit f6807c4
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 49 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ chrono = { version = "0.4", features = ["serde"] }
chrono-humanize = "0.2.3"
clap = { version = "4.2", features = ["derive"] }
colors-transform = "0.2.11"
content_inspector = "0.2.4"
criterion = "0.5"
directories = "4.0"
futures = "0.3"
Expand Down
1 change: 1 addition & 0 deletions crates/maple_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ chrono = { workspace = true }
chrono-humanize = { workspace = true }
clap = { workspace = true }
colors-transform = { workspace = true }
content_inspector = { workspace = true }
copypasta = { version = "0.10.0", default-features = false, features = [ "x11" ] }
futures = { workspace = true }
# ripgrep for global search
Expand Down
16 changes: 7 additions & 9 deletions crates/maple_core/src/previewer/text_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,9 @@ fn generate_preview_lines(
title_line: String,
max_line_width: usize,
size: usize,
) -> std::io::Result<(Vec<String>, FileSizeTier)> {
let file_size = utils::io::determine_file_size_tier(path.as_ref())?;

let lines = match file_size {
file_size_tier: FileSizeTier,
) -> std::io::Result<Vec<String>> {
let lines = match file_size_tier {
FileSizeTier::Empty | FileSizeTier::Small => {
let lines_iter = read_first_lines(path.as_ref(), size)?;
std::iter::once(title_line)
Expand All @@ -128,23 +127,22 @@ fn generate_preview_lines(
}
};

Ok((lines, file_size))
Ok(lines)
}

pub struct TextLines {
// Preview lines to display.
pub lines: Vec<String>,
// Path to display, potentially truncated.
pub display_path: String,
// Size tier of the file.
pub file_size: FileSizeTier,
}

pub fn preview_file<P: AsRef<Path>>(
path: P,
size: usize,
max_line_width: usize,
max_title_width: Option<usize>,
file_size_tier: FileSizeTier,
) -> std::io::Result<TextLines> {
if !path.as_ref().is_file() {
return Err(std::io::Error::new(
Expand All @@ -161,12 +159,12 @@ pub fn preview_file<P: AsRef<Path>>(
abs_path
};

let (lines, file_size) = generate_preview_lines(path, abs_path.clone(), max_line_width, size)?;
let lines =
generate_preview_lines(path, abs_path.clone(), max_line_width, size, file_size_tier)?;

Ok(TextLines {
lines,
display_path: abs_path,
file_size,
})
}

Expand Down
132 changes: 105 additions & 27 deletions crates/maple_core/src/stdio_server/provider/hooks/on_move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ use crate::stdio_server::plugin::syntax::sublime::{
use crate::stdio_server::provider::{read_dir_entries, Context, ProviderSource};
use crate::stdio_server::vim::{preview_syntax, VimResult};
use crate::tools::ctags::{current_context_tag, BufferTag};
use maple_config::HighlightEngine;
use paths::{expand_tilde, truncate_absolute_path};
use pattern::*;
use serde::{Deserialize, Serialize};
use std::io::{Error, ErrorKind, Result};
use std::io::{Error, ErrorKind, Read, Result};
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::sync::atomic::Ordering;
Expand Down Expand Up @@ -115,6 +116,25 @@ impl Preview {
}
}

fn binary_file_preview(path: impl AsRef<Path>) -> Self {
Self::new_file_preview(
vec!["<Binary file>".to_string()],
None,
VimSyntaxInfo::fname(path.as_ref().display().to_string()),
)
}

fn large_file_preview(size: u64, path: impl AsRef<Path>) -> Self {
let size_in_gib = size as f64 / (1024.0 * 1024.0 * 1024.0);
Self::new_file_preview(
vec![format!(
"File too large to preview (size: {size_in_gib:.2} GiB)."
)],
None,
VimSyntaxInfo::fname(path.as_ref().display().to_string()),
)
}

fn set_highlights(&mut self, highlight_source: HighlightSource, path: &Path) {
match highlight_source {
HighlightSource::Sublime(v) => {
Expand Down Expand Up @@ -421,12 +441,23 @@ impl<'a> CachedPreviewImpl<'a> {
async fn preview_file<P: AsRef<Path>>(&self, path: P) -> Result<Preview> {
let path = path.as_ref();

if !path.is_file() {
return Err(Error::new(
ErrorKind::Other,
format!("Failed to preview as {} is not a file", path.display()),
));
}
let file_size_tier = match detect_file_class(path)? {
FileClass::NotRegularFile => {
return Err(Error::new(
ErrorKind::Other,
format!("Failed to preview as {} is not a file", path.display()),
));
}
FileClass::Binary => {
return Ok(Preview::binary_file_preview(path));
}
FileClass::Text(file_size_tier) => {
if let utils::io::FileSizeTier::Large(size) = file_size_tier {
return Ok(Preview::large_file_preview(size, path));
}
file_size_tier
}
};

let handle_io_error = |e: &Error| {
if e.kind() == ErrorKind::NotFound {
Expand All @@ -437,33 +468,33 @@ impl<'a> CachedPreviewImpl<'a> {
}
};

let (lines, fname, file_size) = match (self.ctx.env.is_nvim, self.ctx.env.has_nvim_09) {
let (lines, fname) = match (self.ctx.env.is_nvim, self.ctx.env.has_nvim_09) {
(true, false) => {
// Title is not available before nvim 0.9
let max_fname_len = self.ctx.env.display_line_width - 1;
let previewer::text_file::TextLines {
lines,
display_path,
file_size,
} = previewer::text_file::preview_file(
path,
self.preview_height,
self.max_line_width(),
Some(max_fname_len),
file_size_tier,
)
.inspect_err(handle_io_error)?;
(lines, display_path, file_size)
(lines, display_path)
}
_ => {
let previewer::text_file::TextLines {
lines,
display_path: abs_path,
file_size,
} = previewer::text_file::preview_file(
path,
self.preview_height,
self.max_line_width(),
None,
file_size_tier,
)
.inspect_err(handle_io_error)?;

Expand All @@ -472,11 +503,11 @@ impl<'a> CachedPreviewImpl<'a> {
let mut lines = lines;
lines[0] = cwd_relative;

(lines, abs_path, file_size)
(lines, abs_path)
}
};

if file_size.is_empty() {
if file_size_tier.is_empty() {
let mut lines = lines;
lines.push("<Empty file>".to_string());
return Ok(Preview::new_file_preview(
Expand All @@ -486,7 +517,7 @@ impl<'a> CachedPreviewImpl<'a> {
));
}

let highlight_source = if file_size.is_small() {
let highlight_source = if file_size_tier.is_small() {
SyntaxHighlighter {
context: HighlightingContext {
lines: lines.clone(),
Expand All @@ -504,11 +535,15 @@ impl<'a> CachedPreviewImpl<'a> {
HighlightSource::None
};

let end = lines.len();

let scrollbar = if file_size.can_process() && self.ctx.env.should_add_scrollbar(end) {
let total = utils::io::line_count(path)?;
compute_scrollbar_position(self.ctx, 0, end, total)
// Only display the scrollbar when it's not a large file.
let scrollbar = if file_size_tier.can_process() {
let end = lines.len();
if self.ctx.env.should_add_scrollbar(end) {
let total = utils::io::line_count(path)?;
compute_scrollbar_position(self.ctx, 0, end, total)
} else {
None
}
} else {
None
};
Expand All @@ -526,7 +561,30 @@ impl<'a> CachedPreviewImpl<'a> {
column_range: Option<Range<usize>>,
container_width: usize,
) -> Preview {
tracing::debug!(path = ?path.display(), lnum, "Previewing file");
tracing::debug!(path = ?path.display(), "Previewing file at line {lnum}");

match detect_file_class(path) {
Ok(FileClass::NotRegularFile) => {
return Preview::new_file_preview(
vec!["<Not a regular file>".to_string()],
None,
VimSyntaxInfo::fname(path.display().to_string()),
);
}
Ok(FileClass::Binary) => return Preview::binary_file_preview(path),
Ok(FileClass::Text(file_size_tier)) => {
if let utils::io::FileSizeTier::Large(size) = file_size_tier {
return Preview::large_file_preview(size, path);
}
}
Err(err) => {
return Preview::new_file_preview(
vec![err.to_string()],
None,
VimSyntaxInfo::fname(path.display().to_string()),
);
}
};

let fname = path.display().to_string();

Expand Down Expand Up @@ -614,11 +672,7 @@ impl<'a> CachedPreviewImpl<'a> {
preview
}
Err(err) => {
tracing::error!(
?path,
provider_id = %self.ctx.provider_id(),
"Couldn't read first lines: {err:?}",
);
tracing::error!(?path, provider_id = %self.ctx.provider_id(), "Couldn't read first lines: {err:?}");
let header_line = truncated_preview_header();
let lines = vec![
header_line,
Expand Down Expand Up @@ -701,6 +755,32 @@ impl<'a> CachedPreviewImpl<'a> {
}
}

enum FileClass {
NotRegularFile,
Binary,
Text(utils::io::FileSizeTier),
}

fn detect_file_class(path: &Path) -> std::io::Result<FileClass> {
if !path.is_file() {
return Ok(FileClass::NotRegularFile);
}

let mut file = std::fs::File::open(&path)?;
let metadata = file.metadata()?;

let mut buf = vec![0u8; 1024];
let n = file.read(&mut buf)?;
let content_type = content_inspector::inspect(&buf[..n]);

if content_type.is_binary() {
return Ok(FileClass::Binary);
}

let file_size_tier = utils::io::FileSizeTier::from_metadata(&metadata);
Ok(FileClass::Text(file_size_tier))
}

async fn fetch_context_tag_with_timeout(path: &Path, lnum: usize) -> Option<BufferTag> {
let (tag_sender, tag_receiver) = oneshot::channel();

Expand Down Expand Up @@ -894,8 +974,6 @@ impl SyntaxHighlighter {
// TODO: this might be slow for larger files (over 100k lines) as tree-sitter will have to
// parse the whole file to obtain the highlight info. We may make the highlighting async.
fn compute_syntax_highlighting(context: HighlightingContext) -> HighlightSource {
use maple_config::HighlightEngine;

let HighlightingContext {
lines,
path,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,19 @@ impl RecentFilesProvider {
let preview = match (preview_size, ranked.get(lnum - 1)) {
(Some(size), Some(new_entry)) => {
let new_curline = new_entry.display_text().to_string();
let file_size_tier = utils::io::FileSizeTier::from_metadata(
&std::fs::File::open(&new_curline)?.metadata()?,
);
if let Ok(text_lines) = crate::previewer::text_file::preview_file(
new_curline,
size,
self.printer.line_width,
None,
file_size_tier,
) {
let crate::previewer::text_file::TextLines {
lines,
display_path: fname,
..
} = text_lines;
Some(json!({ "lines": lines, "fname": fname }))
} else {
Expand Down
17 changes: 14 additions & 3 deletions crates/maple_core/src/stdio_server/request_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@ pub async fn preview_file(msg: RpcRequest) -> Result<Value, Error> {
(display_height, preview_width.unwrap_or(display_width))
};

let file_size_tier =
utils::io::FileSizeTier::from_metadata(&std::fs::File::open(&fpath)?.metadata()?);

let TextLines {
lines,
display_path: fname,
..
} = crate::previewer::text_file::preview_file(fpath, preview_height, preview_width, None)?;
} = crate::previewer::text_file::preview_file(
fpath,
preview_height,
preview_width,
None,
file_size_tier,
)?;

let value = json!({"id": msg_id, "result": json!({"lines": lines, "fname": fname})});

Expand Down Expand Up @@ -73,7 +81,10 @@ pub async fn preview_quickfix(msg: RpcRequest) -> Result<Value, Error> {

let result = if lnum == 0 {
let size = winheight + 5;
let TextLines { lines, .. } = preview_file(fpath.as_path(), size, winwidth, None)?;
let file_size_tier =
utils::io::FileSizeTier::from_metadata(&std::fs::File::open(&fpath)?.metadata()?);
let TextLines { lines, .. } =
preview_file(fpath.as_path(), size, winwidth, None, file_size_tier)?;
json!({ "event": "on_move", "lines": lines, "fname": fpath })
} else {
let (lines, hi_lnum) = preview_file_at(fpath.as_path(), winheight, winwidth, lnum)?;
Expand Down
Loading

0 comments on commit f6807c4

Please sign in to comment.