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