]> git.lizzy.rs Git - rust.git/blob - crates/ra_lsp_server/src/server_world.rs
thread info about dep names
[rust.git] / crates / ra_lsp_server / src / server_world.rs
1 use std::{
2     fs,
3     path::{Path, PathBuf},
4     sync::Arc,
5 };
6
7 use languageserver_types::Url;
8 use ra_analysis::{
9     Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FileResolver, LibraryData,
10 };
11 use rustc_hash::FxHashMap;
12 use failure::{bail, format_err};
13
14 use crate::{
15     path_map::{PathMap, Root},
16     project_model::{CargoWorkspace, TargetKind},
17     vfs::{FileEvent, FileEventKind},
18     Result,
19 };
20
21 #[derive(Debug, Default)]
22 pub struct ServerWorldState {
23     pub workspaces: Arc<Vec<CargoWorkspace>>,
24     pub analysis_host: AnalysisHost,
25     pub path_map: PathMap,
26     pub mem_map: FxHashMap<FileId, Option<String>>,
27 }
28
29 pub struct ServerWorld {
30     pub workspaces: Arc<Vec<CargoWorkspace>>,
31     pub analysis: Analysis,
32     pub path_map: PathMap,
33 }
34
35 impl ServerWorldState {
36     pub fn apply_fs_changes(&mut self, events: Vec<FileEvent>) {
37         let mut change = AnalysisChange::new();
38         let mut inserted = false;
39         {
40             let pm = &mut self.path_map;
41             let mm = &mut self.mem_map;
42             events
43                 .into_iter()
44                 .map(|event| {
45                     let text = match event.kind {
46                         FileEventKind::Add(text) => text,
47                     };
48                     (event.path, text)
49                 })
50                 .map(|(path, text)| {
51                     let (ins, file_id) = pm.get_or_insert(path, Root::Workspace);
52                     inserted |= ins;
53                     (file_id, text)
54                 })
55                 .filter_map(|(file_id, text)| {
56                     if mm.contains_key(&file_id) {
57                         mm.insert(file_id, Some(text));
58                         None
59                     } else {
60                         Some((file_id, text))
61                     }
62                 })
63                 .for_each(|(file_id, text)| change.add_file(file_id, text));
64         }
65         if inserted {
66             change.set_file_resolver(Arc::new(self.path_map.clone()))
67         }
68         self.analysis_host.apply_change(change);
69     }
70     pub fn events_to_files(
71         &mut self,
72         events: Vec<FileEvent>,
73     ) -> (Vec<(FileId, String)>, Arc<FileResolver>) {
74         let files = {
75             let pm = &mut self.path_map;
76             events
77                 .into_iter()
78                 .map(|event| {
79                     let FileEventKind::Add(text) = event.kind;
80                     (event.path, text)
81                 })
82                 .map(|(path, text)| (pm.get_or_insert(path, Root::Lib).1, text))
83                 .collect()
84         };
85         let resolver = Arc::new(self.path_map.clone());
86         (files, resolver)
87     }
88     pub fn add_lib(&mut self, data: LibraryData) {
89         let mut change = AnalysisChange::new();
90         change.add_library(data);
91         self.analysis_host.apply_change(change);
92     }
93
94     pub fn add_mem_file(&mut self, path: PathBuf, text: String) -> FileId {
95         let (inserted, file_id) = self.path_map.get_or_insert(path, Root::Workspace);
96         if self.path_map.get_root(file_id) != Root::Lib {
97             let mut change = AnalysisChange::new();
98             if inserted {
99                 change.add_file(file_id, text);
100                 change.set_file_resolver(Arc::new(self.path_map.clone()));
101             } else {
102                 change.change_file(file_id, text);
103             }
104             self.analysis_host.apply_change(change);
105         }
106         self.mem_map.insert(file_id, None);
107         file_id
108     }
109
110     pub fn change_mem_file(&mut self, path: &Path, text: String) -> Result<()> {
111         let file_id = self
112             .path_map
113             .get_id(path)
114             .ok_or_else(|| format_err!("change to unknown file: {}", path.display()))?;
115         if self.path_map.get_root(file_id) != Root::Lib {
116             let mut change = AnalysisChange::new();
117             change.change_file(file_id, text);
118             self.analysis_host.apply_change(change);
119         }
120         Ok(())
121     }
122
123     pub fn remove_mem_file(&mut self, path: &Path) -> Result<FileId> {
124         let file_id = self
125             .path_map
126             .get_id(path)
127             .ok_or_else(|| format_err!("change to unknown file: {}", path.display()))?;
128         match self.mem_map.remove(&file_id) {
129             Some(_) => (),
130             None => bail!("unmatched close notification"),
131         };
132         // Do this via file watcher ideally.
133         let text = fs::read_to_string(path).ok();
134         if self.path_map.get_root(file_id) != Root::Lib {
135             let mut change = AnalysisChange::new();
136             if let Some(text) = text {
137                 change.change_file(file_id, text);
138             }
139             self.analysis_host.apply_change(change);
140         }
141         Ok(file_id)
142     }
143     pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) {
144         let mut crate_graph = CrateGraph::default();
145         let mut pkg_to_lib_crate = FxHashMap::default();
146         let mut pkg_crates = FxHashMap::default();
147         for ws in ws.iter() {
148             for pkg in ws.packages() {
149                 for tgt in pkg.targets(ws) {
150                     let root = tgt.root(ws);
151                     if let Some(file_id) = self.path_map.get_id(root) {
152                         let crate_id = crate_graph.add_crate_root(file_id);
153                         if tgt.kind(ws) == TargetKind::Lib {
154                             pkg_to_lib_crate.insert(pkg, crate_id);
155                         }
156                         pkg_crates
157                             .entry(pkg)
158                             .or_insert_with(Vec::new)
159                             .push(crate_id);
160                     }
161                 }
162             }
163             for pkg in ws.packages() {
164                 for dep in pkg.dependencies(ws) {
165                     if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
166                         for &from in pkg_crates.get(&pkg).into_iter().flatten() {
167                             crate_graph.add_dep(from, dep.name.clone(), to);
168                         }
169                     }
170                 }
171             }
172         }
173         self.workspaces = Arc::new(ws);
174         let mut change = AnalysisChange::new();
175         change.set_crate_graph(crate_graph);
176         self.analysis_host.apply_change(change);
177     }
178     pub fn snapshot(&self) -> ServerWorld {
179         ServerWorld {
180             workspaces: Arc::clone(&self.workspaces),
181             analysis: self.analysis_host.analysis(),
182             path_map: self.path_map.clone(),
183         }
184     }
185 }
186
187 impl ServerWorld {
188     pub fn analysis(&self) -> &Analysis {
189         &self.analysis
190     }
191
192     pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
193         let path = uri
194             .to_file_path()
195             .map_err(|()| format_err!("invalid uri: {}", uri))?;
196         self.path_map
197             .get_id(&path)
198             .ok_or_else(|| format_err!("unknown file: {}", path.display()))
199     }
200
201     pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
202         let path = self.path_map.get_path(id);
203         let url = Url::from_file_path(path)
204             .map_err(|()| format_err!("can't convert path to url: {}", path.display()))?;
205         Ok(url)
206     }
207 }