diff options
author | Aleksey Kladov <[email protected]> | 2018-12-18 09:29:14 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-12-20 09:15:23 +0000 |
commit | 7509901fa0985f8fc4893a83e0275a063f072dda (patch) | |
tree | c39da5c7459d633772c50df1135a3653cb506903 /crates/ra_vfs | |
parent | dbb62b5baa36783bbca06f09c77794fce3ceff0c (diff) |
wip
Diffstat (limited to 'crates/ra_vfs')
-rw-r--r-- | crates/ra_vfs/Cargo.toml | 11 | ||||
-rw-r--r-- | crates/ra_vfs/src/arena.rs | 48 | ||||
-rw-r--r-- | crates/ra_vfs/src/io.rs | 72 | ||||
-rw-r--r-- | crates/ra_vfs/src/lib.rs | 128 |
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] | ||
2 | edition = "2018" | ||
3 | name = "ra_vfs" | ||
4 | version = "0.1.0" | ||
5 | authors = ["Aleksey Kladov <[email protected]>"] | ||
6 | |||
7 | [dependencies] | ||
8 | walkdir = "2.2.7" | ||
9 | relative-path = "0.4.0" | ||
10 | rustc-hash = "1.0" | ||
11 | crossbeam-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 @@ | |||
1 | use std::{ | ||
2 | hash::{Hash, Hasher}, | ||
3 | marker::PhantomData, | ||
4 | ops::{Index, IndexMut}, | ||
5 | }; | ||
6 | |||
7 | #[derive(Clone, Debug)] | ||
8 | pub(crate) struct Arena<ID: ArenaId, T> { | ||
9 | data: Vec<T>, | ||
10 | _ty: PhantomData<ID>, | ||
11 | } | ||
12 | |||
13 | pub(crate) trait ArenaId { | ||
14 | fn from_u32(id: u32) -> Self; | ||
15 | fn to_u32(self) -> u32; | ||
16 | } | ||
17 | |||
18 | impl<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 | |||
26 | impl<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 | |||
35 | impl<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 | |||
43 | impl<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 @@ | |||
1 | use std::{ | ||
2 | fs, | ||
3 | path::{Path, PathBuf}, | ||
4 | thread::JoinHandle, | ||
5 | }; | ||
6 | |||
7 | use walkdir::WalkDir; | ||
8 | use crossbeam_channel::{Sender, Receiver}; | ||
9 | |||
10 | pub(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. | ||
14 | mod arena; | ||
15 | mod io; | ||
16 | |||
17 | use std::{ | ||
18 | cmp::Reverse, | ||
19 | path::{Path, PathBuf}, | ||
20 | ffi::OsStr, | ||
21 | sync::Arc, | ||
22 | }; | ||
23 | |||
24 | use relative_path::RelativePathBuf; | ||
25 | use crate::arena::{ArenaId, Arena}; | ||
26 | |||
27 | /// `RootFilter` is a predicate that checks if a file can belong to a root | ||
28 | struct RootFilter { | ||
29 | root: PathBuf, | ||
30 | file_filter: fn(&Path) -> bool, | ||
31 | } | ||
32 | |||
33 | impl 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 | |||
45 | fn rs_extension_filter(p: &Path) -> bool { | ||
46 | p.extension() == Some(OsStr::new("rs")) | ||
47 | } | ||
48 | |||
49 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
50 | pub struct VfsRoot(u32); | ||
51 | |||
52 | impl 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)] | ||
62 | pub struct VfsFile(u32); | ||
63 | |||
64 | impl 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 | |||
73 | struct VfsFileData { | ||
74 | root: VfsRoot, | ||
75 | path: RelativePathBuf, | ||
76 | text: Arc<String>, | ||
77 | } | ||
78 | |||
79 | #[derive(Default)] | ||
80 | struct Vfs { | ||
81 | roots: Arena<VfsRoot, RootFilter>, | ||
82 | files: Arena<VfsFile, VfsFileData>, | ||
83 | // pending_changes: Vec<PendingChange>, | ||
84 | } | ||
85 | |||
86 | impl 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)] | ||
110 | pub 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 | } | ||