aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src/world.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_lsp_server/src/world.rs')
-rw-r--r--crates/ra_lsp_server/src/world.rs314
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 96efab844..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. In our
2//! server implementation this is know as the `WorldState`.
3//!
4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
5
6use std::{
7 path::{Path, PathBuf},
8 sync::Arc,
9};
10
11use crossbeam_channel::{unbounded, Receiver};
12use lsp_server::ErrorCode;
13use lsp_types::Url;
14use parking_lot::RwLock;
15use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher};
16use ra_ide::{
17 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData,
18 SourceRootId,
19};
20use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace};
21use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
22use relative_path::RelativePathBuf;
23
24use 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)]
32pub 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)]
47pub 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.
62pub 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
71impl 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
246impl 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}