Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@
## 2025-05-19 - File Discovery Allocations
**Learning:** In `discover_importing_files`, `WalkBuilder` results were being converted to `PathBuf` via `.map(|e| e.into_path())` *before* filtering. This caused allocations for every single file in the workspace (including excluded files and directories).
**Action:** Filter `ignore::DirEntry` directly using `entry.file_type()` and `entry.path()` before mapping to `PathBuf`. This avoids allocations for non-matching files.

## 2025-06-05 - Avoid extra stat calls during directory traversal
**Learning:** Calling `entry.path().is_file()` during directory traversal with crates like `ignore` performs an unnecessary `stat` syscall. Using `entry.file_type().is_some_and(|ft| ft.is_file())` utilizes metadata already retrieved during `readdir`, which is significantly faster.
**Action:** When filtering files during traversal, always use the metadata provided by the directory entry (`file_type()`) rather than querying the filesystem again using the path.
3 changes: 2 additions & 1 deletion crates/mill-ast/src/import_updater/file_scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ pub async fn find_project_files(

for entry in walker.flatten() {
let path = entry.path();
if path.is_file() {
// ⚑ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls
if entry.file_type().is_some_and(|ft| ft.is_file()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve symlinked files during scans

When the workspace contains a symlink to a source file, this changes behavior: Path::is_file() follows the symlink and previously included it, while DirEntry::file_type() reports the directory entry itself and returns a symlink file type when WalkBuilder is not configured with follow_links(true). As a result, symlinked project files are now skipped by import scanning, so imports in those files will not be updated; the same replacement appears in the other changed walkers as well. Consider using entry.file_type().is_some_and(|ft| ft.is_file()) || entry.path().is_file() (or explicitly following links) if symlinked files should remain supported.

Useful? React with πŸ‘Β / πŸ‘Ž.

if let Some(ext) = path.extension() {
let ext_str = ext.to_str().unwrap_or("");
// Check if any plugin handles this extension
Expand Down
3 changes: 2 additions & 1 deletion crates/mill-handlers/src/handlers/common/checksums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ pub async fn calculate_checksums_for_directory_rename(
.build();

for entry in walker.flatten() {
if entry.path().is_file() {
// ⚑ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls
if entry.file_type().is_some_and(|ft| ft.is_file()) {
if let Ok(content) = context.app_state.file_service.read_file(entry.path()).await {
file_checksums.insert(
entry.path().to_string_lossy().to_string(),
Expand Down
3 changes: 2 additions & 1 deletion crates/mill-handlers/src/handlers/lsp_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,8 @@ impl LspImportFinder for DirectLspAdapter {
let mut files_in_dir = Vec::new();
for entry in walker.flatten() {
let path = entry.path();
if path.is_file() {
// ⚑ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls
if entry.file_type().is_some_and(|ft| ft.is_file()) {
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
if self.extensions.contains(&ext.to_string()) {
files_in_dir.push(path.to_path_buf());
Expand Down
3 changes: 2 additions & 1 deletion crates/mill-handlers/src/handlers/prune_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,8 @@ impl PrunePlanner {
let walker = ignore::WalkBuilder::new(&abs_dir).hidden(false).build();
let files = walker
.flatten()
.filter(|entry| entry.path().is_file())
// ⚑ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls
.filter(|entry| entry.file_type().is_some_and(|ft| ft.is_file()))
.map(|entry| entry.path().to_path_buf())
.collect();
Ok((files, abs_dir))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,8 @@ impl RenameService {
let mut files_to_move = 0;
let walker = ignore::WalkBuilder::new(&old_path).hidden(false).build();
for entry in walker.flatten() {
if entry.path().is_file() {
// ⚑ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls
if entry.file_type().is_some_and(|ft| ft.is_file()) {
files_to_move += 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,8 @@ impl FileService {
let mut files = Vec::new();
let walker = ignore::WalkBuilder::new(dir).hidden(false).build();
for entry in walker.flatten() {
if entry.path().is_file() {
// ⚑ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls
if entry.file_type().is_some_and(|ft| ft.is_file()) {
files.push(entry.path().to_path_buf());
}
}
Expand Down
11 changes: 8 additions & 3 deletions crates/mill-services/src/services/move_service/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,10 @@ async fn append_svelte_import_edits(

for entry in walker.flatten() {
let path = entry.path();
if path.is_file() && path.extension().and_then(|e| e.to_str()) == Some("svelte") {
// ⚑ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls
if entry.file_type().is_some_and(|ft| ft.is_file())
&& path.extension().and_then(|e| e.to_str()) == Some("svelte")
{
files.push(path.to_path_buf());
}
}
Expand Down Expand Up @@ -420,7 +423,8 @@ async fn plan_documentation_and_config_edits(

for entry in moved_walker.flatten() {
let path = entry.path();
if path.is_file() {
// ⚑ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls
if entry.file_type().is_some_and(|ft| ft.is_file()) {
if let Ok(relative) = path.strip_prefix(old_path) {
moved_files.push((path.to_path_buf(), new_path.join(relative)));
}
Expand Down Expand Up @@ -455,7 +459,8 @@ async fn plan_documentation_and_config_edits(

for entry in walker.flatten() {
let path = entry.path();
if path.is_file() {
// ⚑ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls
if entry.file_type().is_some_and(|ft| ft.is_file()) {
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
if file_extensions.contains(ext) {
files_by_extension
Expand Down
Loading