Skip to content

Commit ea2e631

Browse files
committed
feature(zubanls): ✨ wasm support for zubanls
wasm language server with a subset of capabilities, ty #137 for `local_fs_stub` & `memfs`
1 parent befa201 commit ea2e631

File tree

14 files changed

+652
-30
lines changed

14 files changed

+652
-30
lines changed

Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ toml_edit = "*"
4848
tracing = "*"
4949
tracing-subscriber = { version = "*", features = ["time", "local-time"] }
5050
tracing-appender = "*"
51+
wasm-bindgen = "0.2"
5152

5253
# Dev dependencies
5354
insta = "*"

crates/vfs/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ utils.workspace = true
1515

1616
anyhow.workspace = true
1717
tracing.workspace = true
18+
glob = "*"
19+
20+
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
1821
crossbeam-channel.workspace = true
1922
notify.workspace = true
20-
glob = "*"
2123
same-file = "*"
24+
25+
[target.'cfg(target_arch = "wasm32")'.dependencies]
26+
wasm-bindgen.workspace = true

crates/vfs/src/lib.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
// Some parts are copied from rust-analyzer
22

33
mod glob_abs_path;
4+
5+
#[cfg(not(target_arch = "wasm32"))]
6+
mod local_fs;
7+
8+
#[cfg(target_arch = "wasm32")]
9+
#[path = "local_fs_stub.rs"]
410
mod local_fs;
11+
12+
mod memory;
513
mod normalized_path;
614
mod path;
715
mod tree;
@@ -10,18 +18,34 @@ mod workspaces;
1018

1119
use std::{borrow::Cow, path::Path, sync::Arc};
1220

13-
use crossbeam_channel::Receiver;
14-
1521
pub use glob_abs_path::GlobAbsPath;
22+
1623
pub use local_fs::{LocalFS, SimpleLocalFS};
24+
25+
#[cfg(target_arch = "wasm32")]
26+
pub use memory::InMemoryFs;
27+
1728
pub use normalized_path::NormalizedPath;
1829
pub use path::AbsPath;
1930
pub use tree::{DirOrFile, Directory, DirectoryEntry, Entries, FileEntry, FileIndex, Parent};
2031
pub use vfs::{InvalidationResult, PathWithScheme, Vfs, VfsFile, VfsPanicRecovery};
2132
pub use workspaces::{Workspace, WorkspaceKind};
2233

34+
#[cfg(not(target_arch = "wasm32"))]
35+
pub use crossbeam_channel::Receiver;
36+
37+
#[cfg(not(target_arch = "wasm32"))]
2338
pub type NotifyEvent = notify::Result<notify::Event>;
2439

40+
#[cfg(target_arch = "wasm32")]
41+
pub type NotifyEvent = ();
42+
43+
#[cfg(target_arch = "wasm32")]
44+
use std::marker::PhantomData;
45+
46+
#[cfg(target_arch = "wasm32")]
47+
pub type Receiver<T> = PhantomData<T>;
48+
2549
/// Interface for reading and watching files.
2650
pub trait VfsHandler: Sync + Send {
2751
/// Load the content of the given file, returning [`None`] if it does not

crates/vfs/src/local_fs_stub.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::{
2+
path::{Path, PathBuf},
3+
sync::Arc,
4+
};
5+
6+
use crate::{
7+
AbsPath, DirectoryEntry, Entries, NormalizedPath, NotifyEvent, Parent, PathWithScheme,
8+
VfsHandler,
9+
};
10+
11+
pub type SimpleLocalFS = LocalFS;
12+
13+
#[derive(Clone, Default)]
14+
pub struct LocalFS;
15+
16+
impl LocalFS {
17+
pub fn without_watcher() -> Self {
18+
Self
19+
}
20+
pub fn with_watcher<T>(_: T) -> Self {
21+
Self
22+
}
23+
pub fn watch<P: AsRef<Path>>(&self, _: P) {}
24+
pub fn current_dir(&self) -> Arc<AbsPath> {
25+
self.unchecked_abs_path("")
26+
}
27+
pub fn normalized_path_from_current_dir(&self, p: &str) -> Arc<NormalizedPath> {
28+
self.normalize_rc_path(self.absolute_path(&self.current_dir(), p))
29+
}
30+
}
31+
32+
impl VfsHandler for LocalFS {
33+
fn read_and_watch_file(&self, _: &PathWithScheme) -> Option<String> {
34+
None
35+
}
36+
fn notify_receiver(&self) -> Option<&crate::Receiver<NotifyEvent>> {
37+
None
38+
}
39+
fn on_invalidated_in_memory_file(&self, _: PathWithScheme) {}
40+
fn read_and_watch_dir(&self, _: &str, _: Parent) -> Entries {
41+
Entries::from_vec(vec![])
42+
}
43+
fn read_and_watch_entry(&self, _: &str, _: Parent, _: &str) -> Option<DirectoryEntry> {
44+
None
45+
}
46+
fn separator(&self) -> char {
47+
'/'
48+
}
49+
fn split_off_folder<'a>(&self, path: &'a str) -> (&'a str, Option<&'a str>) {
50+
if path.is_empty() {
51+
return (path, None);
52+
}
53+
if let Some(pos) = path
54+
.as_bytes()
55+
.iter()
56+
.position(|b| *b == b'/' || *b == b'\\')
57+
{
58+
let (head, tail) = path.split_at(pos);
59+
(
60+
head,
61+
if tail.len() > 1 {
62+
Some(&tail[1..])
63+
} else {
64+
None
65+
},
66+
)
67+
} else {
68+
(path, None)
69+
}
70+
}
71+
fn join(&self, path: &AbsPath, name: &str) -> Arc<AbsPath> {
72+
let p = Path::new(&**path);
73+
let joined = if p.as_os_str().is_empty() {
74+
PathBuf::from(name)
75+
} else {
76+
p.join(name)
77+
};
78+
self.unchecked_abs_path(joined.to_str().unwrap_or(name))
79+
}
80+
fn is_case_sensitive(&self) -> bool {
81+
true
82+
}
83+
}

0 commit comments

Comments
 (0)