Skip to content

Conversation

sxyazi
Copy link
Owner

@sxyazi sxyazi commented Aug 27, 2025

No description provided.

@sxyazi sxyazi requested a review from Copilot August 27, 2025 07:47
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces casefold functionality to handle case-insensitive file operations on various filesystems. The changes refactor the existing case validation system to provide more robust case-insensitive filename handling.

  • Replaces valid_name_case with casefold function that returns the actual cased filename
  • Adds platform-specific implementations for casefold operations across Unix-like systems and Windows
  • Updates filesystem operations to use the new casefold functionality for proper case handling

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
yazi-watcher/src/watcher.rs Updates file watcher to use new case matching function
yazi-fs/src/provider/provider.rs Adds public casefold function to provider interface
yazi-fs/src/provider/local/local.rs Updates parameter names for consistency
yazi-fs/src/provider/local/casefold.rs Replaces case validation with comprehensive casefold implementation
yazi-fs/src/fns.rs Removes old realname functionality and associated tests
yazi-actor/src/mgr/rename.rs Updates rename operations to use casefold for proper case handling
yazi-actor/src/mgr/create.rs Updates file creation to use casefold functionality
cspell.json Adds "inodes" to dictionary

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +22 to +26
use std::{ffi::{CStr, CString, OsString}, os::{fd::{AsRawFd, FromRawFd, OwnedFd}, unix::ffi::OsStringExt}};

use libc::{F_GETPATH, O_RDONLY, O_SYMLINK, PATH_MAX};

let cstr = CString::new(path.into_os_string().into_encoded_bytes())?;
let Some(name) = Path::new(OsStr::from_bytes(cstr.as_bytes())).file_name() else {
return Ok(true);
};

let cstr = CString::new(path.into_os_string().into_vec())?;
Copy link
Preview

Copilot AI Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The into_vec() conversion on line 26 and the subsequent vector operations could be inefficient for large paths. Consider using byte slice operations where possible to avoid unnecessary allocations.

Copilot uses AI. Check for mistakes.

Comment on lines +71 to +86
let mut entries: Vec<_> = std::fs::read_dir(parent)?
.filter_map(Result::ok)
.filter_map(|e| e.metadata().ok().map(|m| (e, m)))
.filter(|(_, m)| m.dev() == meta.dev() && m.ino() == meta.ino())
.map(|(e, _)| e.path())
.collect();

if entries.len() == 1 {
// No hardlink that shares the same inode
Ok(entries.remove(0))
} else if let Some(i) = entries.iter().position(|p| p == path) {
// Exact match
Ok(entries.swap_remove(i))
} else if let Some(i) = entries.iter().position(|p| p.as_os_str().eq_ignore_ascii_case(oss)) {
// Case-insensitive match
Ok(entries.swap_remove(i))
Copy link
Preview

Copilot AI Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading the entire directory and filtering by inode could be inefficient for large directories. This creates an O(n) operation for each casefold call. Consider caching directory contents or using a more efficient lookup strategy.

Suggested change
let mut entries: Vec<_> = std::fs::read_dir(parent)?
.filter_map(Result::ok)
.filter_map(|e| e.metadata().ok().map(|m| (e, m)))
.filter(|(_, m)| m.dev() == meta.dev() && m.ino() == meta.ino())
.map(|(e, _)| e.path())
.collect();
if entries.len() == 1 {
// No hardlink that shares the same inode
Ok(entries.remove(0))
} else if let Some(i) = entries.iter().position(|p| p == path) {
// Exact match
Ok(entries.swap_remove(i))
} else if let Some(i) = entries.iter().position(|p| p.as_os_str().eq_ignore_ascii_case(oss)) {
// Case-insensitive match
Ok(entries.swap_remove(i))
let mut inode_map: HashMap<(u64, u64), Vec<PathBuf>> = HashMap::new();
for entry in std::fs::read_dir(parent)? {
if let Ok(e) = entry {
if let Ok(m) = e.metadata() {
let key = (m.dev(), m.ino());
inode_map.entry(key).or_default().push(e.path());
}
}
}
let key = (meta.dev(), meta.ino());
let entries = inode_map.get_mut(&key);
if let Some(entries) = entries {
if entries.len() == 1 {
// No hardlink that shares the same inode
Ok(entries.remove(0))
} else if let Some(i) = entries.iter().position(|p| p == path) {
// Exact match
Ok(entries.swap_remove(i))
} else if let Some(i) = entries.iter().position(|p| p.as_os_str().eq_ignore_ascii_case(oss)) {
// Case-insensitive match
Ok(entries.swap_remove(i))
} else {
Err(io::Error::new(io::ErrorKind::NotFound, "file not found"))
}

Copilot uses AI. Check for mistakes.

Comment on lines +78 to +86
if entries.len() == 1 {
// No hardlink that shares the same inode
Ok(entries.remove(0))
} else if let Some(i) = entries.iter().position(|p| p == path) {
// Exact match
Ok(entries.swap_remove(i))
} else if let Some(i) = entries.iter().position(|p| p.as_os_str().eq_ignore_ascii_case(oss)) {
// Case-insensitive match
Ok(entries.swap_remove(i))
Copy link
Preview

Copilot AI Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The logic for handling different numbers of entries (1, exact match, case-insensitive match) could be simplified and made more readable by restructuring the conditional flow.

Suggested change
if entries.len() == 1 {
// No hardlink that shares the same inode
Ok(entries.remove(0))
} else if let Some(i) = entries.iter().position(|p| p == path) {
// Exact match
Ok(entries.swap_remove(i))
} else if let Some(i) = entries.iter().position(|p| p.as_os_str().eq_ignore_ascii_case(oss)) {
// Case-insensitive match
Ok(entries.swap_remove(i))
// Prefer exact match, then case-insensitive match, then unique inode match
if let Some(i) = entries.iter().position(|p| p == path) {
// Exact match
Ok(entries[i].clone())
} else if let Some(i) = entries.iter().position(|p| p.as_os_str().eq_ignore_ascii_case(oss)) {
// Case-insensitive match
Ok(entries[i].clone())
} else if entries.len() == 1 {
// Only one entry with matching inode
Ok(entries[0].clone())

Copilot uses AI. Check for mistakes.

@sxyazi sxyazi merged commit c2a94da into main Aug 27, 2025
6 checks passed
@sxyazi sxyazi deleted the pr-07c30baf branch August 27, 2025 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant