Implement dragging external files to remote projects (#28987)

Release Notes:

- Added the ability to copy external files into remote projects by
dragging them onto the project panel.

---------

Co-authored-by: Peter Tripp <petertripp@gmail.com>
This commit is contained in:
Max Brunsfeld 2025-04-17 11:06:56 -07:00 committed by GitHub
parent fade49a11a
commit 7e928dd615
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 275 additions and 82 deletions

View file

@ -111,6 +111,7 @@ pub trait Fs: Send + Sync {
async fn load_bytes(&self, path: &Path) -> Result<Vec<u8>>;
async fn atomic_write(&self, path: PathBuf, text: String) -> Result<()>;
async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()>;
async fn write(&self, path: &Path, content: &[u8]) -> Result<()>;
async fn canonicalize(&self, path: &Path) -> Result<PathBuf>;
async fn is_file(&self, path: &Path) -> bool;
async fn is_dir(&self, path: &Path) -> bool;
@ -567,6 +568,14 @@ impl Fs for RealFs {
Ok(())
}
async fn write(&self, path: &Path, content: &[u8]) -> Result<()> {
if let Some(path) = path.parent() {
self.create_dir(path).await?;
}
smol::fs::write(path, content).await?;
Ok(())
}
async fn canonicalize(&self, path: &Path) -> Result<PathBuf> {
Ok(smol::fs::canonicalize(path).await?)
}
@ -2105,6 +2114,16 @@ impl Fs for FakeFs {
Ok(())
}
async fn write(&self, path: &Path, content: &[u8]) -> Result<()> {
self.simulate_random_delay().await;
let path = normalize_path(path);
if let Some(path) = path.parent() {
self.create_dir(path).await?;
}
self.write_file_internal(path, content.to_vec(), false)?;
Ok(())
}
async fn canonicalize(&self, path: &Path) -> Result<PathBuf> {
let path = normalize_path(path);
self.simulate_random_delay().await;
@ -2346,7 +2365,7 @@ pub async fn copy_recursive<'a>(
target: &'a Path,
options: CopyOptions,
) -> Result<()> {
for (is_dir, item) in read_dir_items(fs, source).await? {
for (item, is_dir) in read_dir_items(fs, source).await? {
let Ok(item_relative_path) = item.strip_prefix(source) else {
continue;
};
@ -2380,7 +2399,10 @@ pub async fn copy_recursive<'a>(
Ok(())
}
async fn read_dir_items<'a>(fs: &'a dyn Fs, source: &'a Path) -> Result<Vec<(bool, PathBuf)>> {
/// Recursively reads all of the paths in the given directory.
///
/// Returns a vector of tuples of (path, is_dir).
pub async fn read_dir_items<'a>(fs: &'a dyn Fs, source: &'a Path) -> Result<Vec<(PathBuf, bool)>> {
let mut items = Vec::new();
read_recursive(fs, source, &mut items).await?;
Ok(items)
@ -2389,7 +2411,7 @@ async fn read_dir_items<'a>(fs: &'a dyn Fs, source: &'a Path) -> Result<Vec<(boo
fn read_recursive<'a>(
fs: &'a dyn Fs,
source: &'a Path,
output: &'a mut Vec<(bool, PathBuf)>,
output: &'a mut Vec<(PathBuf, bool)>,
) -> BoxFuture<'a, Result<()>> {
use futures::future::FutureExt;
@ -2400,7 +2422,7 @@ fn read_recursive<'a>(
.ok_or_else(|| anyhow!("path does not exist: {}", source.display()))?;
if metadata.is_dir {
output.push((true, source.to_path_buf()));
output.push((source.to_path_buf(), true));
let mut children = fs.read_dir(source).await?;
while let Some(child_path) = children.next().await {
if let Ok(child_path) = child_path {
@ -2408,7 +2430,7 @@ fn read_recursive<'a>(
}
}
} else {
output.push((false, source.to_path_buf()));
output.push((source.to_path_buf(), false));
}
Ok(())
}