diff options
Diffstat (limited to 'crates/ra_lsp_server/src/world.rs')
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 314 |
1 files changed, 0 insertions, 314 deletions
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs deleted file mode 100644 index 71c95d4af..000000000 --- a/crates/ra_lsp_server/src/world.rs +++ /dev/null | |||
@@ -1,314 +0,0 @@ | |||
1 | //! The context or environment in which the language server functions. | ||
2 | //! In our server implementation this is know as the `WorldState`. | ||
3 | //! | ||
4 | //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. | ||
5 | |||
6 | use std::{ | ||
7 | path::{Path, PathBuf}, | ||
8 | sync::Arc, | ||
9 | }; | ||
10 | |||
11 | use crossbeam_channel::{unbounded, Receiver}; | ||
12 | use lsp_server::ErrorCode; | ||
13 | use lsp_types::Url; | ||
14 | use parking_lot::RwLock; | ||
15 | use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; | ||
16 | use ra_ide::{ | ||
17 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, | ||
18 | SourceRootId, | ||
19 | }; | ||
20 | use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; | ||
21 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; | ||
22 | use relative_path::RelativePathBuf; | ||
23 | |||
24 | use crate::{ | ||
25 | diagnostics::{CheckFixes, DiagnosticCollection}, | ||
26 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, | ||
27 | vfs_glob::{Glob, RustPackageFilterBuilder}, | ||
28 | LspError, Result, | ||
29 | }; | ||
30 | |||
31 | #[derive(Debug, Clone)] | ||
32 | pub struct Options { | ||
33 | pub publish_decorations: bool, | ||
34 | pub supports_location_link: bool, | ||
35 | pub line_folding_only: bool, | ||
36 | pub max_inlay_hint_length: Option<usize>, | ||
37 | pub rustfmt_args: Vec<String>, | ||
38 | pub cargo_watch: CheckOptions, | ||
39 | } | ||
40 | |||
41 | /// `WorldState` is the primary mutable state of the language server | ||
42 | /// | ||
43 | /// The most interesting components are `vfs`, which stores a consistent | ||
44 | /// snapshot of the file systems, and `analysis_host`, which stores our | ||
45 | /// incremental salsa database. | ||
46 | #[derive(Debug)] | ||
47 | pub struct WorldState { | ||
48 | pub options: Options, | ||
49 | //FIXME: this belongs to `LoopState` rather than to `WorldState` | ||
50 | pub roots_to_scan: usize, | ||
51 | pub roots: Vec<PathBuf>, | ||
52 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | ||
53 | pub analysis_host: AnalysisHost, | ||
54 | pub vfs: Arc<RwLock<Vfs>>, | ||
55 | pub task_receiver: Receiver<VfsTask>, | ||
56 | pub latest_requests: Arc<RwLock<LatestRequests>>, | ||
57 | pub check_watcher: CheckWatcher, | ||
58 | pub diagnostics: DiagnosticCollection, | ||
59 | } | ||
60 | |||
61 | /// An immutable snapshot of the world's state at a point in time. | ||
62 | pub struct WorldSnapshot { | ||
63 | pub options: Options, | ||
64 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | ||
65 | pub analysis: Analysis, | ||
66 | pub latest_requests: Arc<RwLock<LatestRequests>>, | ||
67 | pub check_fixes: CheckFixes, | ||
68 | vfs: Arc<RwLock<Vfs>>, | ||
69 | } | ||
70 | |||
71 | impl WorldState { | ||
72 | pub fn new( | ||
73 | folder_roots: Vec<PathBuf>, | ||
74 | workspaces: Vec<ProjectWorkspace>, | ||
75 | lru_capacity: Option<usize>, | ||
76 | exclude_globs: &[Glob], | ||
77 | watch: Watch, | ||
78 | options: Options, | ||
79 | feature_flags: FeatureFlags, | ||
80 | ) -> WorldState { | ||
81 | let mut change = AnalysisChange::new(); | ||
82 | |||
83 | let mut roots = Vec::new(); | ||
84 | roots.extend(folder_roots.iter().map(|path| { | ||
85 | let mut filter = RustPackageFilterBuilder::default().set_member(true); | ||
86 | for glob in exclude_globs.iter() { | ||
87 | filter = filter.exclude(glob.clone()); | ||
88 | } | ||
89 | RootEntry::new(path.clone(), filter.into_vfs_filter()) | ||
90 | })); | ||
91 | for ws in workspaces.iter() { | ||
92 | roots.extend(ws.to_roots().into_iter().map(|pkg_root| { | ||
93 | let mut filter = | ||
94 | RustPackageFilterBuilder::default().set_member(pkg_root.is_member()); | ||
95 | for glob in exclude_globs.iter() { | ||
96 | filter = filter.exclude(glob.clone()); | ||
97 | } | ||
98 | RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter()) | ||
99 | })); | ||
100 | } | ||
101 | let (task_sender, task_receiver) = unbounded(); | ||
102 | let task_sender = Box::new(move |t| task_sender.send(t).unwrap()); | ||
103 | let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch); | ||
104 | let roots_to_scan = vfs_roots.len(); | ||
105 | for r in vfs_roots { | ||
106 | let vfs_root_path = vfs.root2path(r); | ||
107 | let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); | ||
108 | change.add_root(SourceRootId(r.0), is_local); | ||
109 | change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); | ||
110 | } | ||
111 | |||
112 | // FIXME: Read default cfgs from config | ||
113 | let default_cfg_options = { | ||
114 | let mut opts = get_rustc_cfg_options(); | ||
115 | opts.insert_atom("test".into()); | ||
116 | opts.insert_atom("debug_assertion".into()); | ||
117 | opts | ||
118 | }; | ||
119 | |||
120 | // Create crate graph from all the workspaces | ||
121 | let mut crate_graph = CrateGraph::default(); | ||
122 | let mut load = |path: &std::path::Path| { | ||
123 | let vfs_file = vfs.load(path); | ||
124 | vfs_file.map(|f| FileId(f.0)) | ||
125 | }; | ||
126 | for ws in workspaces.iter() { | ||
127 | let (graph, crate_names) = ws.to_crate_graph(&default_cfg_options, &mut load); | ||
128 | let shift = crate_graph.extend(graph); | ||
129 | for (crate_id, name) in crate_names { | ||
130 | change.set_debug_crate_name(crate_id.shift(shift), name) | ||
131 | } | ||
132 | } | ||
133 | change.set_crate_graph(crate_graph); | ||
134 | |||
135 | // FIXME: Figure out the multi-workspace situation | ||
136 | let check_watcher = workspaces | ||
137 | .iter() | ||
138 | .find_map(|w| match w { | ||
139 | ProjectWorkspace::Cargo { cargo, .. } => Some(cargo), | ||
140 | ProjectWorkspace::Json { .. } => None, | ||
141 | }) | ||
142 | .map(|cargo| { | ||
143 | let cargo_project_root = cargo.workspace_root().to_path_buf(); | ||
144 | CheckWatcher::new(&options.cargo_watch, cargo_project_root) | ||
145 | }) | ||
146 | .unwrap_or_else(|| { | ||
147 | log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); | ||
148 | CheckWatcher::dummy() | ||
149 | }); | ||
150 | |||
151 | let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags); | ||
152 | analysis_host.apply_change(change); | ||
153 | WorldState { | ||
154 | options, | ||
155 | roots_to_scan, | ||
156 | roots: folder_roots, | ||
157 | workspaces: Arc::new(workspaces), | ||
158 | analysis_host, | ||
159 | vfs: Arc::new(RwLock::new(vfs)), | ||
160 | task_receiver, | ||
161 | latest_requests: Default::default(), | ||
162 | check_watcher, | ||
163 | diagnostics: Default::default(), | ||
164 | } | ||
165 | } | ||
166 | |||
167 | /// Returns a vec of libraries | ||
168 | /// FIXME: better API here | ||
169 | pub fn process_changes( | ||
170 | &mut self, | ||
171 | ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> { | ||
172 | let changes = self.vfs.write().commit_changes(); | ||
173 | if changes.is_empty() { | ||
174 | return None; | ||
175 | } | ||
176 | let mut libs = Vec::new(); | ||
177 | let mut change = AnalysisChange::new(); | ||
178 | for c in changes { | ||
179 | match c { | ||
180 | VfsChange::AddRoot { root, files } => { | ||
181 | let root_path = self.vfs.read().root2path(root); | ||
182 | let is_local = self.roots.iter().any(|r| root_path.starts_with(r)); | ||
183 | if is_local { | ||
184 | self.roots_to_scan -= 1; | ||
185 | for (file, path, text) in files { | ||
186 | change.add_file(SourceRootId(root.0), FileId(file.0), path, text); | ||
187 | } | ||
188 | } else { | ||
189 | let files = files | ||
190 | .into_iter() | ||
191 | .map(|(vfsfile, path, text)| (FileId(vfsfile.0), path, text)) | ||
192 | .collect(); | ||
193 | libs.push((SourceRootId(root.0), files)); | ||
194 | } | ||
195 | } | ||
196 | VfsChange::AddFile { root, file, path, text } => { | ||
197 | change.add_file(SourceRootId(root.0), FileId(file.0), path, text); | ||
198 | } | ||
199 | VfsChange::RemoveFile { root, file, path } => { | ||
200 | change.remove_file(SourceRootId(root.0), FileId(file.0), path) | ||
201 | } | ||
202 | VfsChange::ChangeFile { file, text } => { | ||
203 | change.change_file(FileId(file.0), text); | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | self.analysis_host.apply_change(change); | ||
208 | Some(libs) | ||
209 | } | ||
210 | |||
211 | pub fn add_lib(&mut self, data: LibraryData) { | ||
212 | self.roots_to_scan -= 1; | ||
213 | let mut change = AnalysisChange::new(); | ||
214 | change.add_library(data); | ||
215 | self.analysis_host.apply_change(change); | ||
216 | } | ||
217 | |||
218 | pub fn snapshot(&self) -> WorldSnapshot { | ||
219 | WorldSnapshot { | ||
220 | options: self.options.clone(), | ||
221 | workspaces: Arc::clone(&self.workspaces), | ||
222 | analysis: self.analysis_host.analysis(), | ||
223 | vfs: Arc::clone(&self.vfs), | ||
224 | latest_requests: Arc::clone(&self.latest_requests), | ||
225 | check_fixes: Arc::clone(&self.diagnostics.check_fixes), | ||
226 | } | ||
227 | } | ||
228 | |||
229 | pub fn maybe_collect_garbage(&mut self) { | ||
230 | self.analysis_host.maybe_collect_garbage() | ||
231 | } | ||
232 | |||
233 | pub fn collect_garbage(&mut self) { | ||
234 | self.analysis_host.collect_garbage() | ||
235 | } | ||
236 | |||
237 | pub fn complete_request(&mut self, request: CompletedRequest) { | ||
238 | self.latest_requests.write().record(request) | ||
239 | } | ||
240 | |||
241 | pub fn feature_flags(&self) -> &FeatureFlags { | ||
242 | self.analysis_host.feature_flags() | ||
243 | } | ||
244 | } | ||
245 | |||
246 | impl WorldSnapshot { | ||
247 | pub fn analysis(&self) -> &Analysis { | ||
248 | &self.analysis | ||
249 | } | ||
250 | |||
251 | pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> { | ||
252 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | ||
253 | let file = self.vfs.read().path2file(&path).ok_or_else(|| { | ||
254 | // Show warning as this file is outside current workspace | ||
255 | LspError { | ||
256 | code: ErrorCode::InvalidRequest as i32, | ||
257 | message: "Rust file outside current workspace is not supported yet.".to_string(), | ||
258 | } | ||
259 | })?; | ||
260 | Ok(FileId(file.0)) | ||
261 | } | ||
262 | |||
263 | pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { | ||
264 | let path = self.vfs.read().file2path(VfsFile(id.0)); | ||
265 | let url = url_from_path_with_drive_lowercasing(path)?; | ||
266 | |||
267 | Ok(url) | ||
268 | } | ||
269 | |||
270 | pub fn file_id_to_path(&self, id: FileId) -> PathBuf { | ||
271 | self.vfs.read().file2path(VfsFile(id.0)) | ||
272 | } | ||
273 | |||
274 | pub fn file_line_endings(&self, id: FileId) -> LineEndings { | ||
275 | self.vfs.read().file_line_endings(VfsFile(id.0)) | ||
276 | } | ||
277 | |||
278 | pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> { | ||
279 | let base = self.vfs.read().root2path(VfsRoot(root.0)); | ||
280 | let path = path.to_path(base); | ||
281 | let url = Url::from_file_path(&path) | ||
282 | .map_err(|_| format!("can't convert path to url: {}", path.display()))?; | ||
283 | Ok(url) | ||
284 | } | ||
285 | |||
286 | pub fn status(&self) -> String { | ||
287 | let mut res = String::new(); | ||
288 | if self.workspaces.is_empty() { | ||
289 | res.push_str("no workspaces\n") | ||
290 | } else { | ||
291 | res.push_str("workspaces:\n"); | ||
292 | for w in self.workspaces.iter() { | ||
293 | res += &format!("{} packages loaded\n", w.n_packages()); | ||
294 | } | ||
295 | } | ||
296 | res.push_str("\nanalysis:\n"); | ||
297 | res.push_str( | ||
298 | &self | ||
299 | .analysis | ||
300 | .status() | ||
301 | .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()), | ||
302 | ); | ||
303 | res | ||
304 | } | ||
305 | |||
306 | pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> { | ||
307 | let path = self.vfs.read().file2path(VfsFile(file_id.0)); | ||
308 | self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path)) | ||
309 | } | ||
310 | |||
311 | pub fn feature_flags(&self) -> &FeatureFlags { | ||
312 | self.analysis.feature_flags() | ||
313 | } | ||
314 | } | ||