aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_vfs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_vfs')
-rw-r--r--crates/ra_vfs/Cargo.toml11
-rw-r--r--crates/ra_vfs/src/arena.rs48
-rw-r--r--crates/ra_vfs/src/io.rs72
-rw-r--r--crates/ra_vfs/src/lib.rs128
4 files changed, 259 insertions, 0 deletions
diff --git a/crates/ra_vfs/Cargo.toml b/crates/ra_vfs/Cargo.toml
new file mode 100644
index 000000000..f8d49b3f5
--- /dev/null
+++ b/crates/ra_vfs/Cargo.toml
@@ -0,0 +1,11 @@
1[package]
2edition = "2018"
3name = "ra_vfs"
4version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"]
6
7[dependencies]
8walkdir = "2.2.7"
9relative-path = "0.4.0"
10rustc-hash = "1.0"
11crossbeam-channel = "0.2.4"
diff --git a/crates/ra_vfs/src/arena.rs b/crates/ra_vfs/src/arena.rs
new file mode 100644
index 000000000..d6fad753b
--- /dev/null
+++ b/crates/ra_vfs/src/arena.rs
@@ -0,0 +1,48 @@
1use std::{
2 hash::{Hash, Hasher},
3 marker::PhantomData,
4 ops::{Index, IndexMut},
5};
6
7#[derive(Clone, Debug)]
8pub(crate) struct Arena<ID: ArenaId, T> {
9 data: Vec<T>,
10 _ty: PhantomData<ID>,
11}
12
13pub(crate) trait ArenaId {
14 fn from_u32(id: u32) -> Self;
15 fn to_u32(self) -> u32;
16}
17
18impl<ID: ArenaId, T> Arena<ID, T> {
19 pub fn alloc(&mut self, value: T) -> ID {
20 let id = self.data.len() as u32;
21 self.data.push(value);
22 ID::from_u32(id)
23 }
24}
25
26impl<ID: ArenaId, T> Default for Arena<ID, T> {
27 fn default() -> Arena<ID, T> {
28 Arena {
29 data: Vec::new(),
30 _ty: PhantomData,
31 }
32 }
33}
34
35impl<ID: ArenaId, T> Index<ID> for Arena<ID, T> {
36 type Output = T;
37 fn index(&self, idx: ID) -> &T {
38 let idx = idx.to_u32() as usize;
39 &self.data[idx]
40 }
41}
42
43impl<ID: ArenaId, T> IndexMut<ID> for Arena<ID, T> {
44 fn index_mut(&mut self, idx: ID) -> &mut T {
45 let idx = idx.to_u32() as usize;
46 &mut self.data[idx]
47 }
48}
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs
new file mode 100644
index 000000000..f90fe0e84
--- /dev/null
+++ b/crates/ra_vfs/src/io.rs
@@ -0,0 +1,72 @@
1use std::{
2 fs,
3 path::{Path, PathBuf},
4 thread::JoinHandle,
5};
6
7use walkdir::WalkDir;
8use crossbeam_channel::{Sender, Receiver};
9
10pub(crate) fn start_io() -> (JoinHandle<(), Sender<()>, Receiver()>) {}
11
12// use crate::thread_watcher::{ThreadWatcher, Worker};
13
14// #[derive(Debug)]
15// pub struct FileEvent {
16// pub path: PathBuf,
17// pub kind: FileEventKind,
18// }
19
20// #[derive(Debug)]
21// pub enum FileEventKind {
22// Add(String),
23// }
24
25// pub fn roots_loader() -> (Worker<PathBuf, (PathBuf, Vec<FileEvent>)>, ThreadWatcher) {
26// Worker::<PathBuf, (PathBuf, Vec<FileEvent>)>::spawn(
27// "roots loader",
28// 128,
29// |input_receiver, output_sender| {
30// input_receiver
31// .map(|path| {
32// log::debug!("loading {} ...", path.as_path().display());
33// let events = load_root(path.as_path());
34// log::debug!("... loaded {}", path.as_path().display());
35// (path, events)
36// })
37// .for_each(|it| output_sender.send(it))
38// },
39// )
40// }
41
42// fn load_root(path: &Path) -> Vec<FileEvent> {
43// let mut res = Vec::new();
44// for entry in WalkDir::new(path) {
45// let entry = match entry {
46// Ok(entry) => entry,
47// Err(e) => {
48// log::warn!("watcher error: {}", e);
49// continue;
50// }
51// };
52// if !entry.file_type().is_file() {
53// continue;
54// }
55// let path = entry.path();
56// if path.extension().and_then(|os| os.to_str()) != Some("rs") {
57// continue;
58// }
59// let text = match fs::read_to_string(path) {
60// Ok(text) => text,
61// Err(e) => {
62// log::warn!("watcher error: {}", e);
63// continue;
64// }
65// };
66// res.push(FileEvent {
67// path: path.to_owned(),
68// kind: FileEventKind::Add(text),
69// })
70// }
71// res
72// }
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
new file mode 100644
index 000000000..8f6abadb7
--- /dev/null
+++ b/crates/ra_vfs/src/lib.rs
@@ -0,0 +1,128 @@
1//! VFS stands for Virtual File System.
2//!
3//! When doing analysis, we don't want to do any IO, we want to keep all source
4//! code in memory. However, the actual source code is stored on disk, so you
5//! need to get it into the memory in the first place somehow. VFS is the
6//! component which does this.
7//!
8//! It also is responsible for watching the disk for changes, and for merging
9//! editor state (modified, unsaved files) with disk state.
10//!
11//! VFS is based on a concept of roots: a set of directories on the file system
12//! whihc are watched for changes. Typically, there will be a root for each
13//! Cargo package.
14mod arena;
15mod io;
16
17use std::{
18 cmp::Reverse,
19 path::{Path, PathBuf},
20 ffi::OsStr,
21 sync::Arc,
22};
23
24use relative_path::RelativePathBuf;
25use crate::arena::{ArenaId, Arena};
26
27/// `RootFilter` is a predicate that checks if a file can belong to a root
28struct RootFilter {
29 root: PathBuf,
30 file_filter: fn(&Path) -> bool,
31}
32
33impl RootFilter {
34 fn new(root: PathBuf) -> RootFilter {
35 RootFilter {
36 root,
37 file_filter: rs_extension_filter,
38 }
39 }
40 fn can_contain(&self, path: &Path) -> bool {
41 (self.file_filter)(path) && path.starts_with(&self.root)
42 }
43}
44
45fn rs_extension_filter(p: &Path) -> bool {
46 p.extension() == Some(OsStr::new("rs"))
47}
48
49#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
50pub struct VfsRoot(u32);
51
52impl ArenaId for VfsRoot {
53 fn from_u32(idx: u32) -> VfsRoot {
54 VfsRoot(idx)
55 }
56 fn to_u32(self) -> u32 {
57 self.0
58 }
59}
60
61#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
62pub struct VfsFile(u32);
63
64impl ArenaId for VfsFile {
65 fn from_u32(idx: u32) -> VfsFile {
66 VfsFile(idx)
67 }
68 fn to_u32(self) -> u32 {
69 self.0
70 }
71}
72
73struct VfsFileData {
74 root: VfsRoot,
75 path: RelativePathBuf,
76 text: Arc<String>,
77}
78
79#[derive(Default)]
80struct Vfs {
81 roots: Arena<VfsRoot, RootFilter>,
82 files: Arena<VfsFile, VfsFileData>,
83 // pending_changes: Vec<PendingChange>,
84}
85
86impl Vfs {
87 pub fn new(mut roots: Vec<PathBuf>) -> Vfs {
88 let mut res = Vfs::default();
89
90 roots.sort_by_key(|it| Reverse(it.as_os_str().len()));
91
92 for path in roots {
93 res.roots.alloc(RootFilter::new(path));
94 }
95 res
96 }
97
98 pub fn add_file_overlay(&mut self, path: &Path, content: String) {}
99
100 pub fn change_file_overlay(&mut self, path: &Path, new_content: String) {}
101
102 pub fn remove_file_overlay(&mut self, path: &Path) {}
103
104 pub fn commit_changes(&mut self) -> Vec<VfsChange> {
105 unimplemented!()
106 }
107}
108
109#[derive(Debug, Clone)]
110pub enum VfsChange {
111 AddRoot {
112 root: VfsRoot,
113 files: Vec<(VfsFile, RelativePathBuf, Arc<String>)>,
114 },
115 AddFile {
116 file: VfsFile,
117 root: VfsRoot,
118 path: RelativePathBuf,
119 text: Arc<String>,
120 },
121 RemoveFile {
122 file: VfsFile,
123 },
124 ChangeFile {
125 file: VfsFile,
126 text: Arc<String>,
127 },
128}