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