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