]> git.lizzy.rs Git - rust.git/blob - crates/ra_lsp_server/src/server_world.rs
Merge #150
[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, CrateGraph, CrateId, 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         {
43             let pm = &mut self.path_map;
44             let mm = &mut self.mem_map;
45             let changes = events
46                 .into_iter()
47                 .map(|event| {
48                     let text = match event.kind {
49                         FileEventKind::Add(text) => Some(text),
50                     };
51                     (event.path, text)
52                 })
53                 .map(|(path, text)| (pm.get_or_insert(path, Root::Workspace), text))
54                 .filter_map(|(id, text)| {
55                     if mm.contains_key(&id) {
56                         mm.insert(id, text);
57                         None
58                     } else {
59                         Some((id, text))
60                     }
61                 });
62             self.analysis_host.change_files(changes);
63         }
64         self.analysis_host
65             .set_file_resolver(Arc::new(self.path_map.clone()));
66     }
67     pub fn events_to_files(
68         &mut self,
69         events: Vec<FileEvent>,
70     ) -> (Vec<(FileId, String)>, Arc<FileResolver>) {
71         let files = {
72             let pm = &mut self.path_map;
73             events
74                 .into_iter()
75                 .map(|event| {
76                     let FileEventKind::Add(text) = event.kind;
77                     (event.path, text)
78                 })
79                 .map(|(path, text)| (pm.get_or_insert(path, Root::Lib), text))
80                 .collect()
81         };
82         let resolver = Arc::new(self.path_map.clone());
83         (files, resolver)
84     }
85     pub fn add_lib(&mut self, data: LibraryData) {
86         self.analysis_host.add_library(data);
87     }
88
89     pub fn add_mem_file(&mut self, path: PathBuf, text: String) -> FileId {
90         let file_id = self.path_map.get_or_insert(path, Root::Workspace);
91         self.analysis_host
92             .set_file_resolver(Arc::new(self.path_map.clone()));
93         self.mem_map.insert(file_id, None);
94         if self.path_map.get_root(file_id) != Root::Lib {
95             self.analysis_host.change_file(file_id, Some(text));
96         }
97         file_id
98     }
99
100     pub fn change_mem_file(&mut self, path: &Path, text: String) -> Result<()> {
101         let file_id = self
102             .path_map
103             .get_id(path)
104             .ok_or_else(|| format_err!("change to unknown file: {}", path.display()))?;
105         if self.path_map.get_root(file_id) != Root::Lib {
106             self.analysis_host.change_file(file_id, Some(text));
107         }
108         Ok(())
109     }
110
111     pub fn remove_mem_file(&mut self, path: &Path) -> Result<FileId> {
112         let file_id = self
113             .path_map
114             .get_id(path)
115             .ok_or_else(|| format_err!("change to unknown file: {}", path.display()))?;
116         match self.mem_map.remove(&file_id) {
117             Some(_) => (),
118             None => bail!("unmatched close notification"),
119         };
120         // Do this via file watcher ideally.
121         let text = fs::read_to_string(path).ok();
122         if self.path_map.get_root(file_id) != Root::Lib {
123             self.analysis_host.change_file(file_id, text);
124         }
125         Ok(file_id)
126     }
127     pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) {
128         let mut crate_roots = FxHashMap::default();
129         ws.iter()
130             .flat_map(|ws| {
131                 ws.packages()
132                     .flat_map(move |pkg| pkg.targets(ws))
133                     .map(move |tgt| tgt.root(ws))
134             })
135             .for_each(|root| {
136                 if let Some(file_id) = self.path_map.get_id(root) {
137                     let crate_id = CrateId(crate_roots.len() as u32);
138                     crate_roots.insert(crate_id, file_id);
139                 }
140             });
141         let crate_graph = CrateGraph { crate_roots };
142         self.workspaces = Arc::new(ws);
143         self.analysis_host.set_crate_graph(crate_graph);
144     }
145     pub fn snapshot(&self) -> ServerWorld {
146         ServerWorld {
147             workspaces: Arc::clone(&self.workspaces),
148             analysis: self.analysis_host.analysis(),
149             path_map: self.path_map.clone(),
150         }
151     }
152 }
153
154 impl ServerWorld {
155     pub fn analysis(&self) -> &Analysis {
156         &self.analysis
157     }
158
159     pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
160         let path = uri
161             .to_file_path()
162             .map_err(|()| format_err!("invalid uri: {}", uri))?;
163         self.path_map
164             .get_id(&path)
165             .ok_or_else(|| format_err!("unknown file: {}", path.display()))
166     }
167
168     pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
169         let path = self.path_map.get_path(id);
170         let url = Url::from_file_path(path)
171             .map_err(|()| format_err!("can't convert path to url: {}", path.display()))?;
172         Ok(url)
173     }
174 }