diff options
Diffstat (limited to 'crates/vfs/src/lib.rs')
-rw-r--r-- | crates/vfs/src/lib.rs | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 2b7b14524..e075d752b 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs | |||
@@ -53,9 +53,15 @@ pub use crate::{ | |||
53 | }; | 53 | }; |
54 | pub use paths::{AbsPath, AbsPathBuf}; | 54 | pub use paths::{AbsPath, AbsPathBuf}; |
55 | 55 | ||
56 | /// Handle to a file in [`Vfs`] | ||
57 | /// | ||
58 | /// Most functions in rust-analyzer use this when they need to refer to a file. | ||
56 | #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | 59 | #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] |
57 | pub struct FileId(pub u32); | 60 | pub struct FileId(pub u32); |
58 | 61 | ||
62 | /// Storage for all files read by rust-analyzer. | ||
63 | /// | ||
64 | /// For more informations see the [crate-level](crate) documentation. | ||
59 | #[derive(Default)] | 65 | #[derive(Default)] |
60 | pub struct Vfs { | 66 | pub struct Vfs { |
61 | interner: PathInterner, | 67 | interner: PathInterner, |
@@ -63,40 +69,73 @@ pub struct Vfs { | |||
63 | changes: Vec<ChangedFile>, | 69 | changes: Vec<ChangedFile>, |
64 | } | 70 | } |
65 | 71 | ||
72 | /// Changed file in the [`Vfs`]. | ||
66 | pub struct ChangedFile { | 73 | pub struct ChangedFile { |
74 | /// Id of the changed file | ||
67 | pub file_id: FileId, | 75 | pub file_id: FileId, |
76 | /// Kind of change | ||
68 | pub change_kind: ChangeKind, | 77 | pub change_kind: ChangeKind, |
69 | } | 78 | } |
70 | 79 | ||
71 | impl ChangedFile { | 80 | impl ChangedFile { |
81 | /// Returns `true` if the change is not [`Delete`](ChangeKind::Delete). | ||
72 | pub fn exists(&self) -> bool { | 82 | pub fn exists(&self) -> bool { |
73 | self.change_kind != ChangeKind::Delete | 83 | self.change_kind != ChangeKind::Delete |
74 | } | 84 | } |
85 | |||
86 | /// Returns `true` if the change is [`Create`](ChangeKind::Create) or | ||
87 | /// [`Delete`](ChangeKind::Delete). | ||
75 | pub fn is_created_or_deleted(&self) -> bool { | 88 | pub fn is_created_or_deleted(&self) -> bool { |
76 | matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete) | 89 | matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete) |
77 | } | 90 | } |
78 | } | 91 | } |
79 | 92 | ||
93 | /// Kind of [file change](ChangedFile). | ||
80 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] | 94 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] |
81 | pub enum ChangeKind { | 95 | pub enum ChangeKind { |
96 | /// The file was (re-)created | ||
82 | Create, | 97 | Create, |
98 | /// The file was modified | ||
83 | Modify, | 99 | Modify, |
100 | /// The file was deleted | ||
84 | Delete, | 101 | Delete, |
85 | } | 102 | } |
86 | 103 | ||
87 | impl Vfs { | 104 | impl Vfs { |
105 | /// Amount of files currently stored. | ||
106 | /// | ||
107 | /// Note that this includes deleted files. | ||
88 | pub fn len(&self) -> usize { | 108 | pub fn len(&self) -> usize { |
89 | self.data.len() | 109 | self.data.len() |
90 | } | 110 | } |
111 | |||
112 | /// Id of the given path if it exists in the `Vfs` and is not deleted. | ||
91 | pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { | 113 | pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { |
92 | self.interner.get(path).filter(|&it| self.get(it).is_some()) | 114 | self.interner.get(path).filter(|&it| self.get(it).is_some()) |
93 | } | 115 | } |
116 | |||
117 | /// File path corresponding to the given `file_id`. | ||
118 | /// | ||
119 | /// # Panics | ||
120 | /// | ||
121 | /// Panics if the id is not present in the `Vfs`. | ||
94 | pub fn file_path(&self, file_id: FileId) -> VfsPath { | 122 | pub fn file_path(&self, file_id: FileId) -> VfsPath { |
95 | self.interner.lookup(file_id).clone() | 123 | self.interner.lookup(file_id).clone() |
96 | } | 124 | } |
125 | |||
126 | /// File content corresponding to the given `file_id`. | ||
127 | /// | ||
128 | /// # Panics | ||
129 | /// | ||
130 | /// Panics if the id is not present in the `Vfs`, or if the corresponding file is | ||
131 | /// deleted. | ||
97 | pub fn file_contents(&self, file_id: FileId) -> &[u8] { | 132 | pub fn file_contents(&self, file_id: FileId) -> &[u8] { |
98 | self.get(file_id).as_deref().unwrap() | 133 | self.get(file_id).as_deref().unwrap() |
99 | } | 134 | } |
135 | |||
136 | /// Returns an iterator over the stored ids and their corresponding paths. | ||
137 | /// | ||
138 | /// This will skip deleted files. | ||
100 | pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ { | 139 | pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ { |
101 | (0..self.data.len()) | 140 | (0..self.data.len()) |
102 | .map(|it| FileId(it as u32)) | 141 | .map(|it| FileId(it as u32)) |
@@ -106,6 +145,13 @@ impl Vfs { | |||
106 | (file_id, path) | 145 | (file_id, path) |
107 | }) | 146 | }) |
108 | } | 147 | } |
148 | |||
149 | /// Update the `path` with the given `contents`. `None` means the file was deleted. | ||
150 | /// | ||
151 | /// Returns `true` if the file was modified, and saves the [change](ChangedFile). | ||
152 | /// | ||
153 | /// If the path does not currently exists in the `Vfs`, allocates a new | ||
154 | /// [`FileId`] for it. | ||
109 | pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool { | 155 | pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool { |
110 | let file_id = self.alloc_file_id(path); | 156 | let file_id = self.alloc_file_id(path); |
111 | let change_kind = match (&self.get(file_id), &contents) { | 157 | let change_kind = match (&self.get(file_id), &contents) { |
@@ -120,12 +166,24 @@ impl Vfs { | |||
120 | self.changes.push(ChangedFile { file_id, change_kind }); | 166 | self.changes.push(ChangedFile { file_id, change_kind }); |
121 | true | 167 | true |
122 | } | 168 | } |
169 | |||
170 | /// Returns `true` if the `Vfs` contains [changes](ChangedFile). | ||
123 | pub fn has_changes(&self) -> bool { | 171 | pub fn has_changes(&self) -> bool { |
124 | !self.changes.is_empty() | 172 | !self.changes.is_empty() |
125 | } | 173 | } |
174 | |||
175 | /// Drain and returns all the changes in the `Vfs`. | ||
126 | pub fn take_changes(&mut self) -> Vec<ChangedFile> { | 176 | pub fn take_changes(&mut self) -> Vec<ChangedFile> { |
127 | mem::take(&mut self.changes) | 177 | mem::take(&mut self.changes) |
128 | } | 178 | } |
179 | |||
180 | /// Returns the id associated with `path` | ||
181 | /// | ||
182 | /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a | ||
183 | /// deleted file; | ||
184 | /// - Else, returns `path`'s id. | ||
185 | /// | ||
186 | /// Does not record a change. | ||
129 | fn alloc_file_id(&mut self, path: VfsPath) -> FileId { | 187 | fn alloc_file_id(&mut self, path: VfsPath) -> FileId { |
130 | let file_id = self.interner.intern(path); | 188 | let file_id = self.interner.intern(path); |
131 | let idx = file_id.0 as usize; | 189 | let idx = file_id.0 as usize; |
@@ -133,9 +191,21 @@ impl Vfs { | |||
133 | self.data.resize_with(len, || None); | 191 | self.data.resize_with(len, || None); |
134 | file_id | 192 | file_id |
135 | } | 193 | } |
194 | |||
195 | /// Returns the content associated with the given `file_id`. | ||
196 | /// | ||
197 | /// # Panics | ||
198 | /// | ||
199 | /// Panics if no file is associated to that id. | ||
136 | fn get(&self, file_id: FileId) -> &Option<Vec<u8>> { | 200 | fn get(&self, file_id: FileId) -> &Option<Vec<u8>> { |
137 | &self.data[file_id.0 as usize] | 201 | &self.data[file_id.0 as usize] |
138 | } | 202 | } |
203 | |||
204 | /// Mutably returns the content associated with the given `file_id`. | ||
205 | /// | ||
206 | /// # Panics | ||
207 | /// | ||
208 | /// Panics if no file is associated to that id. | ||
139 | fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> { | 209 | fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> { |
140 | &mut self.data[file_id.0 as usize] | 210 | &mut self.data[file_id.0 as usize] |
141 | } | 211 | } |