From e33e8a678cd6e192a9ee5e45f61bbe5f04acb532 Mon Sep 17 00:00:00 2001 From: Tatesa Uradnik Date: Mon, 16 Jun 2025 17:12:55 +0200 Subject: [PATCH] feat: add option tto ignore binary files --- book/src/editor.md | 1 + helix-term/src/commands.rs | 2 +- helix-term/src/lib.rs | 30 +++++++++++++++++++++++-- helix-term/src/ui/mod.rs | 6 +++-- helix-term/src/ui/picker.rs | 15 ++++--------- helix-term/tests/test/commands/write.rs | 2 +- helix-term/tests/test/helpers.rs | 2 +- helix-view/src/editor.rs | 4 ++++ 8 files changed, 44 insertions(+), 18 deletions(-) diff --git a/book/src/editor.md b/book/src/editor.md index 667a7147c..4823964bd 100644 --- a/book/src/editor.md +++ b/book/src/editor.md @@ -195,6 +195,7 @@ All git related options are only enabled in a git repository. | Key | Description | Default | |--|--|---------| |`hidden` | Enables ignoring hidden files | `true` +|`binary` | Enables ignoring binary files | `false` |`follow-symlinks` | Follow symlinks instead of ignoring them | `true` |`deduplicate-links` | Ignore symlinks that point at files already shown in the picker | `true` |`parents` | Enables reading ignore files from parent directories | `true` diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 2cbdeb451..c4a8b0cc7 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2552,7 +2552,7 @@ fn global_search(cx: &mut Context) { .git_exclude(config.file_picker_config.git_exclude) .max_depth(config.file_picker_config.max_depth) .filter_entry(move |entry| { - filter_picker_entry(entry, &absolute_root, dedup_symlinks) + filter_picker_entry(entry, &absolute_root, dedup_symlinks, false) }) .add_custom_ignore_filename(helix_loader::config_dir().join("ignore")) .add_custom_ignore_filename(".helix/ignore") diff --git a/helix-term/src/lib.rs b/helix-term/src/lib.rs index a371dd1cf..8d5af779e 100644 --- a/helix-term/src/lib.rs +++ b/helix-term/src/lib.rs @@ -12,7 +12,11 @@ pub mod job; pub mod keymap; pub mod ui; -use std::path::Path; +use std::{ + fs::File, + io::{self, Read}, + path::Path, +}; use futures_util::Future; mod handlers; @@ -44,8 +48,24 @@ fn true_color() -> bool { } } +fn is_binary(path: &Path, read_buffer: &mut Vec) -> io::Result { + let content_type = File::open(path).and_then(|file| { + // Read up to 1kb to detect the content type + let n = file.take(1024).read_to_end(read_buffer)?; + let content_type = content_inspector::inspect(&read_buffer[..n]); + read_buffer.clear(); + Ok(content_type) + })?; + Ok(content_type.is_binary()) +} + /// Function used for filtering dir entries in the various file pickers. -fn filter_picker_entry(entry: &DirEntry, root: &Path, dedup_symlinks: bool) -> bool { +fn filter_picker_entry( + entry: &DirEntry, + root: &Path, + dedup_symlinks: bool, + ignore_binary_files: bool, +) -> bool { // We always want to ignore popular VCS directories, otherwise if // `ignore` is turned off, we end up with a lot of noise // in our picker. @@ -66,6 +86,12 @@ fn filter_picker_entry(entry: &DirEntry, root: &Path, dedup_symlinks: bool) -> b .is_some_and(|path| !path.starts_with(root)); } + if ignore_binary_files { + if let Ok(is_binary) = is_binary(entry.path(), &mut Vec::new()) { + return !is_binary; + } + } + true } diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 106bfbfb8..162ca19e8 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -205,8 +205,8 @@ pub fn file_picker(editor: &Editor, root: PathBuf) -> FilePicker { let now = Instant::now(); let dedup_symlinks = config.file_picker.deduplicate_links; + let ignore_binaries = config.file_picker.binary_files; let absolute_root = root.canonicalize().unwrap_or_else(|_| root.clone()); - let mut walk_builder = WalkBuilder::new(&root); walk_builder .hidden(config.file_picker.hidden) @@ -218,7 +218,9 @@ pub fn file_picker(editor: &Editor, root: PathBuf) -> FilePicker { .git_exclude(config.file_picker.git_exclude) .sort_by_file_name(|name1, name2| name1.cmp(name2)) .max_depth(config.file_picker.max_depth) - .filter_entry(move |entry| filter_picker_entry(entry, &absolute_root, dedup_symlinks)); + .filter_entry(move |entry| { + filter_picker_entry(entry, &absolute_root, dedup_symlinks, ignore_binaries) + }); walk_builder.add_custom_ignore_filename(helix_loader::config_dir().join("ignore")); walk_builder.add_custom_ignore_filename(".helix/ignore"); diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 3f3aaba2b..23b96ecd5 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -4,7 +4,7 @@ mod query; use crate::{ alt, compositor::{self, Component, Compositor, Context, Event, EventResult}, - ctrl, key, shift, + ctrl, is_binary, key, shift, ui::{ self, document::{render_document, LinePos, TextRenderer}, @@ -31,7 +31,6 @@ use tui::widgets::Widget; use std::{ borrow::Cow, collections::HashMap, - io::Read, path::Path, sync::{ atomic::{self, AtomicUsize}, @@ -612,17 +611,11 @@ impl Picker { if metadata.len() > MAX_FILE_SIZE_FOR_PREVIEW { return Ok(CachedPreview::LargeFile); } - let content_type = std::fs::File::open(&path).and_then(|file| { - // Read up to 1kb to detect the content type - let n = file.take(1024).read_to_end(&mut self.read_buffer)?; - let content_type = - content_inspector::inspect(&self.read_buffer[..n]); - self.read_buffer.clear(); - Ok(content_type) - })?; - if content_type.is_binary() { + + if is_binary(&path, &mut self.read_buffer)? { return Ok(CachedPreview::Binary); } + let mut doc = Document::open( &path, None, diff --git a/helix-term/tests/test/commands/write.rs b/helix-term/tests/test/commands/write.rs index 4b78e14c4..6f2d61258 100644 --- a/helix-term/tests/test/commands/write.rs +++ b/helix-term/tests/test/commands/write.rs @@ -746,7 +746,7 @@ async fn test_hardlink_write() -> anyhow::Result<()> { async fn edit_file_with_content(file_content: &[u8]) -> anyhow::Result<()> { let mut file = tempfile::NamedTempFile::new()?; - file.as_file_mut().write_all(&file_content)?; + file.as_file_mut().write_all(file_content)?; helpers::test_key_sequence( &mut helpers::AppBuilder::new() diff --git a/helix-term/tests/test/helpers.rs b/helix-term/tests/test/helpers.rs index ef910852c..7ca210ea8 100644 --- a/helix-term/tests/test/helpers.rs +++ b/helix-term/tests/test/helpers.rs @@ -425,7 +425,7 @@ pub fn reload_file(file: &mut NamedTempFile) -> anyhow::Result<()> { let f = std::fs::OpenOptions::new() .write(true) .read(true) - .open(&path)?; + .open(path)?; *file.as_file_mut() = f; Ok(()) } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 89f053741..6ef55b6af 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -182,6 +182,9 @@ pub struct FilePickerConfig { /// Enables ignoring hidden files. /// Whether to hide hidden files in file picker and global search results. Defaults to true. pub hidden: bool, + /// Enables ignoring binary files. + /// Whether to hide binary files in file picker. Defaults to false. + pub binary_files: bool, /// Enables following symlinks. /// Whether to follow symbolic links in file picker and file or directory completions. Defaults to true. pub follow_symlinks: bool, @@ -218,6 +221,7 @@ impl Default for FilePickerConfig { git_global: true, git_exclude: true, max_depth: None, + binary_files: false, } } }