Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
48 changes: 39 additions & 9 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ members = [

"tooling/perf",
"tooling/workspace-hack",
"tooling/xtask",
"tooling/xtask", "crates/fs_benchmarks", "crates/worktree_benchmarks",
]
default-members = ["crates/zed"]

Expand Down Expand Up @@ -455,6 +455,7 @@ async-compat = "0.2.1"
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-dispatcher = "0.1"
async-fs = "2.1"
async-lock = "2.1"
async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
async-recursion = "1.0.0"
async-tar = "0.5.0"
Expand Down
1 change: 1 addition & 0 deletions crates/editor/src/editor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12509,6 +12509,7 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
)
.await;

cx.run_until_parked();
// Set up a buffer white some trailing whitespace and no trailing newline.
cx.set_state(
&[
Expand Down
93 changes: 67 additions & 26 deletions crates/fs/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod fs_watcher;
use anyhow::{Context as _, Result, anyhow};
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
use ashpd::desktop::trash;
use futures::stream::iter;
use gpui::App;
use gpui::BackgroundExecutor;
use gpui::Global;
Expand Down Expand Up @@ -562,12 +563,17 @@ impl Fs for RealFs {

async fn load(&self, path: &Path) -> Result<String> {
let path = path.to_path_buf();
let text = smol::unblock(|| std::fs::read_to_string(path)).await?;
Ok(text)
self.executor
.spawn(async move { Ok(std::fs::read_to_string(path)?) })
.await
}

async fn load_bytes(&self, path: &Path) -> Result<Vec<u8>> {
let path = path.to_path_buf();
let bytes = smol::unblock(|| std::fs::read(path)).await?;
let bytes = self
.executor
.spawn(async move { std::fs::read(path) })
.await?;
Ok(bytes)
}

Expand Down Expand Up @@ -635,30 +641,46 @@ impl Fs for RealFs {
if let Some(path) = path.parent() {
self.create_dir(path).await?;
}
smol::fs::write(path, content).await?;
Ok(())
let path = path.to_owned();
let contents = content.to_owned();
self.executor
.spawn(async move {
std::fs::write(path, contents)?;
Ok(())
})
.await
}

async fn canonicalize(&self, path: &Path) -> Result<PathBuf> {
Ok(smol::fs::canonicalize(path)
let path = path.to_owned();
self.executor
.spawn(async move {
std::fs::canonicalize(&path).with_context(|| format!("canonicalizing {path:?}"))
})
.await
.with_context(|| format!("canonicalizing {path:?}"))?)
}

async fn is_file(&self, path: &Path) -> bool {
smol::fs::metadata(path)
let path = path.to_owned();
self.executor
.spawn(async move { std::fs::metadata(path).is_ok_and(|metadata| metadata.is_file()) })
.await
.is_ok_and(|metadata| metadata.is_file())
}

async fn is_dir(&self, path: &Path) -> bool {
smol::fs::metadata(path)
let path = path.to_owned();
self.executor
.spawn(async move { std::fs::metadata(path).is_ok_and(|metadata| metadata.is_dir()) })
.await
.is_ok_and(|metadata| metadata.is_dir())
}

async fn metadata(&self, path: &Path) -> Result<Option<Metadata>> {
let symlink_metadata = match smol::fs::symlink_metadata(path).await {
let path_buf = path.to_owned();
let symlink_metadata = match self
.executor
.spawn(async move { std::fs::symlink_metadata(&path_buf) })
.await
{
Ok(metadata) => metadata,
Err(err) => {
return match (err.kind(), err.raw_os_error()) {
Expand All @@ -669,19 +691,28 @@ impl Fs for RealFs {
}
};

let path_buf = path.to_path_buf();
let path_exists = smol::unblock(move || {
path_buf
.try_exists()
.with_context(|| format!("checking existence for path {path_buf:?}"))
})
.await?;
let is_symlink = symlink_metadata.file_type().is_symlink();
let metadata = match (is_symlink, path_exists) {
(true, true) => smol::fs::metadata(path)
.await
.with_context(|| "accessing symlink for path {path}")?,
_ => symlink_metadata,
let metadata = if is_symlink {
let path_buf = path.to_path_buf();
let path_exists = self
.executor
.spawn(async move {
path_buf
.try_exists()
.with_context(|| format!("checking existence for path {path_buf:?}"))
})
.await?;
if path_exists {
let path_buf = path.to_path_buf();
self.executor
.spawn(async move { std::fs::metadata(path_buf) })
.await
.with_context(|| "accessing symlink for path {path}")?
} else {
symlink_metadata
}
} else {
symlink_metadata
};

#[cfg(unix)]
Expand All @@ -707,15 +738,25 @@ impl Fs for RealFs {
}

async fn read_link(&self, path: &Path) -> Result<PathBuf> {
let path = smol::fs::read_link(path).await?;
let path = path.to_owned();
let path = self
.executor
.spawn(async move { std::fs::read_link(&path) })
.await?;
Ok(path)
}

async fn read_dir(
&self,
path: &Path,
) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>> {
let result = smol::fs::read_dir(path).await?.map(|entry| match entry {
let path = path.to_owned();
let result = iter(
self.executor
.spawn(async move { std::fs::read_dir(path) })
.await?,
)
.map(|entry| match entry {
Ok(entry) => Ok(entry.path()),
Err(error) => Err(anyhow!("failed to read dir entry {error:?}")),
});
Expand Down
13 changes: 13 additions & 0 deletions crates/fs_benchmarks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "fs_benchmarks"
version = "0.1.0"
publish.workspace = true
edition.workspace = true

[dependencies]
fs.workspace = true
gpui = {workspace = true, features = ["windows-manifest"]}
workspace-hack.workspace = true

[lints]
workspace = true
1 change: 1 addition & 0 deletions crates/fs_benchmarks/LICENSE-GPL
32 changes: 32 additions & 0 deletions crates/fs_benchmarks/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use fs::Fs;
use gpui::{AppContext, Application};
fn main() {
let Some(path_to_read) = std::env::args().nth(1) else {
println!("Expected path to read as 1st argument.");
return;
};

let _ = Application::headless().run(|cx| {
let fs = fs::RealFs::new(None, cx.background_executor().clone());
cx.background_spawn(async move {
let timer = std::time::Instant::now();
let result = fs.load_bytes(path_to_read.as_ref()).await;
let elapsed = timer.elapsed();
if let Err(e) = result {
println!("Failed `load_bytes` after {elapsed:?} with error `{e}`");
} else {
println!("Took {elapsed:?} to read {} bytes", result.unwrap().len());
};
let timer = std::time::Instant::now();
let result = fs.metadata(path_to_read.as_ref()).await;
let elapsed = timer.elapsed();
if let Err(e) = result {
println!("Failed `metadata` after {elapsed:?} with error `{e}`");
} else {
println!("Took {elapsed:?} to query metadata");
};
std::process::exit(0);
})
.detach();
});
}
1 change: 1 addition & 0 deletions crates/worktree/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ test-support = [

[dependencies]
anyhow.workspace = true
async-lock.workspace = true
clock.workspace = true
collections.workspace = true
fs.workspace = true
Expand Down
Loading
Loading