6 use gen_lsp_server::ErrorCode;
8 use parking_lot::RwLock;
10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
12 use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot};
13 use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
14 use relative_path::RelativePathBuf;
17 main_loop::pending_requests::{CompletedRequest, LatestRequests},
18 project_model::ProjectWorkspace,
22 #[derive(Debug, Clone)]
24 pub publish_decorations: bool,
25 pub show_workspace_loaded: bool,
26 pub supports_location_link: bool,
29 /// `WorldState` is the primary mutable state of the language server
31 /// The most interesting components are `vfs`, which stores a consistent
32 /// snapshot of the file systems, and `analysis_host`, which stores our
33 /// incremental salsa database.
35 pub struct WorldState {
37 pub roots_to_scan: usize,
38 pub roots: Vec<PathBuf>,
39 pub workspaces: Arc<Vec<ProjectWorkspace>>,
40 pub analysis_host: AnalysisHost,
41 pub vfs: Arc<RwLock<Vfs>>,
42 pub latest_requests: Arc<RwLock<LatestRequests>>,
45 /// An immutable snapshot of the world's state at a point in time.
46 pub struct WorldSnapshot {
48 pub workspaces: Arc<Vec<ProjectWorkspace>>,
49 pub analysis: Analysis,
50 pub vfs: Arc<RwLock<Vfs>>,
51 pub latest_requests: Arc<RwLock<LatestRequests>>,
56 folder_roots: Vec<PathBuf>,
57 workspaces: Vec<ProjectWorkspace>,
58 lru_capacity: Option<usize>,
59 exclude_globs: &[Glob],
62 let mut change = AnalysisChange::new();
64 let mut roots = Vec::new();
65 roots.extend(folder_roots.iter().map(|path| {
66 let mut filter = RustPackageFilterBuilder::default().set_member(true);
67 for glob in exclude_globs.iter() {
68 filter = filter.exclude(glob.clone());
70 RootEntry::new(path.clone(), filter.into_vfs_filter())
72 for ws in workspaces.iter() {
73 roots.extend(ws.to_roots().into_iter().map(|pkg_root| {
75 RustPackageFilterBuilder::default().set_member(pkg_root.is_member());
76 for glob in exclude_globs.iter() {
77 filter = filter.exclude(glob.clone());
79 RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter())
83 let (mut vfs, vfs_roots) = Vfs::new(roots);
84 let roots_to_scan = vfs_roots.len();
86 let vfs_root_path = vfs.root2path(r);
87 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
88 change.add_root(SourceRootId(r.0), is_local);
91 // Create crate graph from all the workspaces
92 let mut crate_graph = CrateGraph::default();
93 let mut load = |path: &std::path::Path| {
94 let vfs_file = vfs.load(path);
95 vfs_file.map(|f| FileId(f.0))
97 for ws in workspaces.iter() {
98 crate_graph.extend(ws.to_crate_graph(&mut load));
100 change.set_crate_graph(crate_graph);
102 let mut analysis_host = AnalysisHost::new(lru_capacity);
103 analysis_host.apply_change(change);
108 workspaces: Arc::new(workspaces),
110 vfs: Arc::new(RwLock::new(vfs)),
111 latest_requests: Default::default(),
115 /// Returns a vec of libraries
116 /// FIXME: better API here
117 pub fn process_changes(
119 ) -> Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)> {
120 let changes = self.vfs.write().commit_changes();
121 if changes.is_empty() {
124 let mut libs = Vec::new();
125 let mut change = AnalysisChange::new();
128 VfsChange::AddRoot { root, files } => {
129 let root_path = self.vfs.read().root2path(root);
130 let is_local = self.roots.iter().any(|r| root_path.starts_with(r));
132 self.roots_to_scan -= 1;
133 for (file, path, text) in files {
134 change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
139 .map(|(vfsfile, path, text)| (FileId(vfsfile.0), path, text))
141 libs.push((SourceRootId(root.0), files));
144 VfsChange::AddFile { root, file, path, text } => {
145 change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
147 VfsChange::RemoveFile { root, file, path } => {
148 change.remove_file(SourceRootId(root.0), FileId(file.0), path)
150 VfsChange::ChangeFile { file, text } => {
151 change.change_file(FileId(file.0), text);
155 self.analysis_host.apply_change(change);
159 pub fn add_lib(&mut self, data: LibraryData) {
160 self.roots_to_scan -= 1;
161 let mut change = AnalysisChange::new();
162 change.add_library(data);
163 self.analysis_host.apply_change(change);
166 pub fn snapshot(&self) -> WorldSnapshot {
168 options: self.options.clone(),
169 workspaces: Arc::clone(&self.workspaces),
170 analysis: self.analysis_host.analysis(),
171 vfs: Arc::clone(&self.vfs),
172 latest_requests: Arc::clone(&self.latest_requests),
176 pub fn maybe_collect_garbage(&mut self) {
177 self.analysis_host.maybe_collect_garbage()
180 pub fn collect_garbage(&mut self) {
181 self.analysis_host.collect_garbage()
184 pub fn complete_request(&mut self, request: CompletedRequest) {
185 self.latest_requests.write().record(request)
190 pub fn analysis(&self) -> &Analysis {
194 pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
195 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
196 let file = self.vfs.read().path2file(&path).ok_or_else(|| {
197 // Show warning as this file is outside current workspace
199 code: ErrorCode::InvalidRequest as i32,
200 message: "Rust file outside current workspace is not supported yet.".to_string(),
206 pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
207 let path = self.vfs.read().file2path(VfsFile(id.0));
208 let url = Url::from_file_path(&path)
209 .map_err(|_| format!("can't convert path to url: {}", path.display()))?;
213 pub fn file_line_endings(&self, id: FileId) -> LineEndings {
214 self.vfs.read().file_line_endings(VfsFile(id.0))
217 pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> {
218 let base = self.vfs.read().root2path(VfsRoot(root.0));
219 let path = path.to_path(base);
220 let url = Url::from_file_path(&path)
221 .map_err(|_| format!("can't convert path to url: {}", path.display()))?;
225 pub fn status(&self) -> String {
226 let mut res = String::new();
227 if self.workspaces.is_empty() {
228 res.push_str("no workspaces\n")
230 res.push_str("workspaces:\n");
231 for w in self.workspaces.iter() {
232 res += &format!("{} packages loaded\n", w.n_packages());
235 res.push_str("\nanalysis:\n");
240 .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
245 pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> {
246 let path = self.vfs.read().file2path(VfsFile(file_id.0));
247 self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path))