Skip to content

Commit 782bbfa

Browse files
committed
Fix nested workspaces independent of order, fixes #301
1 parent ff1b197 commit 782bbfa

File tree

5 files changed

+62
-28
lines changed

5 files changed

+62
-28
lines changed

crates/vfs/src/lib.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ pub trait VfsHandler: Sync + Send {
6969
result
7070
}
7171

72-
fn split_off_folder<'a>(&self, path: &'a str) -> (&'a str, Option<&'a str>);
73-
7472
fn normalize_path<'s>(&self, path: &'s AbsPath) -> Cow<'s, NormalizedPath> {
7573
NormalizedPath::normalize(path)
7674
}
@@ -133,6 +131,40 @@ pub trait VfsHandler: Sync + Send {
133131
fn path_relative_to(&self, from: &AbsPath, to: &Path) -> Option<String> {
134132
path_relative_to(from, to, self.separator())
135133
}
134+
135+
fn split_off_first_item<'a>(&self, path: &'a str) -> (&'a str, Option<&'a str>) {
136+
let mut found = path.find(self.separator());
137+
if cfg!(target_os = "windows") {
138+
// Windows allows path with mixed separators
139+
if let Some(found_slash) = path.find('/')
140+
&& found.is_none_or(|found| found <= found_slash)
141+
{
142+
found = Some(found_slash)
143+
}
144+
}
145+
if let Some(pos) = found {
146+
(&path[..pos], Some(&path[pos + 1..]))
147+
} else {
148+
(path, None)
149+
}
150+
}
151+
152+
fn split_off_last_item<'a>(&self, path: &'a str) -> (Option<&'a str>, &'a str) {
153+
let mut found = path.rfind(self.separator());
154+
if cfg!(target_os = "windows") {
155+
// Windows allows path with mixed separators
156+
if let Some(found_slash) = path.rfind('/')
157+
&& found.is_none_or(|found| found >= found_slash)
158+
{
159+
found = Some(found_slash)
160+
}
161+
}
162+
if let Some(pos) = found {
163+
(Some(&path[..pos]), &path[pos + 1..])
164+
} else {
165+
(None, path)
166+
}
167+
}
136168
}
137169

138170
fn path_relative_to(from: &AbsPath, to: &Path, separator: char) -> Option<String> {

crates/vfs/src/local_fs.rs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -164,23 +164,6 @@ impl<T: Fn(PathWithScheme) + Sync + Send> VfsHandler for LocalFS<T> {
164164
self.watcher.as_ref().map(|(_, r)| r)
165165
}
166166

167-
fn split_off_folder<'a>(&self, path: &'a str) -> (&'a str, Option<&'a str>) {
168-
let mut found = path.find(self.separator());
169-
if cfg!(target_os = "windows") {
170-
// Windows allows path with mixed separators
171-
if let Some(found_slash) = path.find('/')
172-
&& found.is_none_or(|found| found <= found_slash)
173-
{
174-
found = Some(found_slash)
175-
}
176-
}
177-
if let Some(pos) = found {
178-
(&path[..pos], Some(&path[pos + 1..]))
179-
} else {
180-
(path, None)
181-
}
182-
}
183-
184167
fn on_invalidated_in_memory_file(&self, path: PathWithScheme) {
185168
if let Some(callback) = self.on_invalidated_in_memory_file.as_ref() {
186169
callback(path)

crates/vfs/src/tree.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ impl Entries {
324324
workspaces: &Workspaces,
325325
path: &str,
326326
) -> Option<DirOrFile> {
327-
let (name, rest) = vfs.split_off_folder(path);
327+
let (name, rest) = vfs.split_off_first_item(path);
328328
if let Some(entry) = self.search(name) {
329329
if let Some(rest) = rest {
330330
if let DirectoryEntry::Directory(dir) = &*entry {
@@ -416,7 +416,7 @@ impl Entries {
416416
}
417417

418418
pub(crate) fn unload_file(&self, vfs: &dyn VfsHandler, path: &str) {
419-
let (name, rest) = vfs.split_off_folder(path);
419+
let (name, rest) = vfs.split_off_first_item(path);
420420
if let Some(entry) = self.search(name) {
421421
if let Some(rest) = rest {
422422
if let DirectoryEntry::Directory(dir) = &*entry
@@ -467,7 +467,7 @@ impl Entries {
467467
}
468468

469469
pub(crate) fn delete_directory(&self, vfs: &dyn VfsHandler, path: &str) -> Result<(), String> {
470-
let (name, rest) = vfs.split_off_folder(path);
470+
let (name, rest) = vfs.split_off_first_item(path);
471471
if let Some(inner) = self.search(name) {
472472
match &*inner {
473473
DirectoryEntry::Directory(dir) => {

crates/vfs/src/workspaces.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use utils::{OwnedMappedReadGuard, match_case};
88
use crate::{
99
AbsPath, DirOrFile, Directory, DirectoryEntry, NormalizedPath, Parent, PathWithScheme,
1010
VfsHandler,
11-
tree::{AddedFile, Entries},
11+
tree::{AddedFile, DirEntries, Entries},
1212
vfs::Scheme,
1313
};
1414

@@ -145,7 +145,7 @@ impl Workspaces {
145145
let mut rest = rest?;
146146
let mut current_dir: Option<Arc<Directory>> = None;
147147
loop {
148-
let (name, new_rest) = vfs.split_off_folder(rest);
148+
let (name, new_rest) = vfs.split_off_first_item(rest);
149149
if let Some(new_rest) = new_rest {
150150
// We generally return None in all cases where the nesting of the path is
151151
// deeper than the current VFS. This is fine for invalidation, since
@@ -392,6 +392,25 @@ impl Workspace {
392392
Parent::Workspace(Arc::downgrade(&workspace)),
393393
);
394394
*workspace.entries.borrow_mut() = std::mem::take(&mut new_entries.borrow_mut());
395+
396+
// Workspaces are added as nested workspaces already for workspaces that are contained
397+
// within this one. But this workspace could be contained by another one, check for that
398+
// here.
399+
if !workspaces.is_empty()
400+
&& let (Some(folder), name) = vfs.split_off_last_item(&workspace.root_path)
401+
{
402+
for old in workspaces {
403+
if ***old.root_path == *folder
404+
&& let Some(dir_entry) = old.entries.search(name)
405+
&& let DirectoryEntry::Directory(dir) = &*dir_entry
406+
{
407+
let result = dir
408+
.entries
409+
.set(DirEntries::NestedWorkspace(Arc::downgrade(&workspace)));
410+
debug_assert!(result.is_ok());
411+
}
412+
}
413+
}
395414
workspace
396415
}
397416

@@ -442,7 +461,7 @@ fn ensure_dirs_and_file(
442461
path: &str,
443462
code: &str,
444463
) -> AddedFile {
445-
let (name, rest) = vfs.split_off_folder(path);
464+
let (name, rest) = vfs.split_off_first_item(path);
446465
if let Some(rest) = rest {
447466
let mut invs = Default::default();
448467
if let Some(x) = entries.search(name) {
@@ -490,8 +509,8 @@ fn strip_path_prefix<'x>(
490509
) -> Option<&'x str> {
491510
let mut to_strip: &str = to_strip;
492511
loop {
493-
let (folder1, rest) = vfs.split_off_folder(path);
494-
let (folder2, rest_to_strip) = vfs.split_off_folder(to_strip);
512+
let (folder1, rest) = vfs.split_off_first_item(path);
513+
let (folder2, rest_to_strip) = vfs.split_off_first_item(to_strip);
495514
if !match_case(case_sensitive, folder1, folder2) {
496515
return None;
497516
}

crates/zmypy/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ mod tests {
798798
#[test]
799799
fn test_read_file_only_once() {
800800
logging_config::setup_logging_for_tests();
801-
for mypy_path in ["['src/inner', 'src']"] {
801+
for mypy_path in ["['src/inner', 'src']", "['src', 'src/inner']"] {
802802
let fixture = format!(
803803
r#"
804804
[file pyproject.toml]

0 commit comments

Comments
 (0)