diff options
Diffstat (limited to 'crates/ra_lsp_server/src/server_world.rs')
-rw-r--r-- | crates/ra_lsp_server/src/server_world.rs | 225 |
1 files changed, 102 insertions, 123 deletions
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index ab4c2c8aa..785877c4b 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs | |||
@@ -1,154 +1,66 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | fs, | 2 | path::{PathBuf}, |
3 | path::{Path, PathBuf}, | ||
4 | sync::Arc, | 3 | sync::Arc, |
5 | }; | 4 | }; |
6 | 5 | ||
7 | use languageserver_types::Url; | 6 | use languageserver_types::Url; |
8 | use ra_analysis::{ | 7 | use ra_analysis::{ |
9 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FileResolver, LibraryData, | 8 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, |
9 | SourceRootId | ||
10 | }; | 10 | }; |
11 | use ra_vfs::{Vfs, VfsChange, VfsFile}; | ||
11 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
12 | use failure::{bail, format_err}; | 13 | use relative_path::RelativePathBuf; |
14 | use parking_lot::RwLock; | ||
15 | use failure::{format_err}; | ||
13 | 16 | ||
14 | use crate::{ | 17 | use crate::{ |
15 | path_map::{PathMap, Root}, | ||
16 | project_model::{CargoWorkspace, TargetKind}, | 18 | project_model::{CargoWorkspace, TargetKind}, |
17 | vfs::{FileEvent, FileEventKind}, | ||
18 | Result, | 19 | Result, |
19 | }; | 20 | }; |
20 | 21 | ||
21 | #[derive(Debug, Default)] | 22 | #[derive(Debug)] |
22 | pub struct ServerWorldState { | 23 | pub struct ServerWorldState { |
24 | pub roots_to_scan: usize, | ||
25 | pub root: PathBuf, | ||
23 | pub workspaces: Arc<Vec<CargoWorkspace>>, | 26 | pub workspaces: Arc<Vec<CargoWorkspace>>, |
24 | pub analysis_host: AnalysisHost, | 27 | pub analysis_host: AnalysisHost, |
25 | pub path_map: PathMap, | 28 | pub vfs: Arc<RwLock<Vfs>>, |
26 | pub mem_map: FxHashMap<FileId, Option<String>>, | ||
27 | } | 29 | } |
28 | 30 | ||
29 | pub struct ServerWorld { | 31 | pub struct ServerWorld { |
30 | pub workspaces: Arc<Vec<CargoWorkspace>>, | 32 | pub workspaces: Arc<Vec<CargoWorkspace>>, |
31 | pub analysis: Analysis, | 33 | pub analysis: Analysis, |
32 | pub path_map: PathMap, | 34 | pub vfs: Arc<RwLock<Vfs>>, |
33 | } | 35 | } |
34 | 36 | ||
35 | impl ServerWorldState { | 37 | impl ServerWorldState { |
36 | pub fn apply_fs_changes(&mut self, events: Vec<FileEvent>) { | 38 | pub fn new(root: PathBuf, workspaces: Vec<CargoWorkspace>) -> ServerWorldState { |
37 | let mut change = AnalysisChange::new(); | 39 | 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 | 40 | ||
94 | pub fn add_mem_file(&mut self, path: PathBuf, text: String) -> FileId { | 41 | let mut roots = Vec::new(); |
95 | let (inserted, file_id) = self.path_map.get_or_insert(path, Root::Workspace); | 42 | roots.push(root.clone()); |
96 | if self.path_map.get_root(file_id) != Root::Lib { | 43 | for ws in workspaces.iter() { |
97 | let mut change = AnalysisChange::new(); | 44 | for pkg in ws.packages() { |
98 | if inserted { | 45 | roots.push(pkg.root(&ws).to_path_buf()); |
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 | } | 46 | } |
104 | self.analysis_host.apply_change(change); | ||
105 | } | 47 | } |
106 | self.mem_map.insert(file_id, None); | 48 | let roots_to_scan = roots.len(); |
107 | file_id | 49 | let (mut vfs, roots) = Vfs::new(roots); |
108 | } | 50 | for r in roots { |
109 | 51 | let is_local = vfs.root2path(r).starts_with(&root); | |
110 | pub fn change_mem_file(&mut self, path: &Path, text: String) -> Result<()> { | 52 | change.add_root(SourceRootId(r.0), is_local); |
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 | } | 53 | } |
120 | Ok(()) | ||
121 | } | ||
122 | 54 | ||
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(); | 55 | let mut crate_graph = CrateGraph::default(); |
145 | let mut pkg_to_lib_crate = FxHashMap::default(); | 56 | let mut pkg_to_lib_crate = FxHashMap::default(); |
146 | let mut pkg_crates = FxHashMap::default(); | 57 | let mut pkg_crates = FxHashMap::default(); |
147 | for ws in ws.iter() { | 58 | for ws in workspaces.iter() { |
148 | for pkg in ws.packages() { | 59 | for pkg in ws.packages() { |
149 | for tgt in pkg.targets(ws) { | 60 | for tgt in pkg.targets(ws) { |
150 | let root = tgt.root(ws); | 61 | let root = tgt.root(ws); |
151 | if let Some(file_id) = self.path_map.get_id(root) { | 62 | if let Some(file_id) = vfs.load(root) { |
63 | let file_id = FileId(file_id.0); | ||
152 | let crate_id = crate_graph.add_crate_root(file_id); | 64 | let crate_id = crate_graph.add_crate_root(file_id); |
153 | if tgt.kind(ws) == TargetKind::Lib { | 65 | if tgt.kind(ws) == TargetKind::Lib { |
154 | pkg_to_lib_crate.insert(pkg, crate_id); | 66 | pkg_to_lib_crate.insert(pkg, crate_id); |
@@ -170,16 +82,80 @@ impl ServerWorldState { | |||
170 | } | 82 | } |
171 | } | 83 | } |
172 | } | 84 | } |
173 | self.workspaces = Arc::new(ws); | ||
174 | let mut change = AnalysisChange::new(); | ||
175 | change.set_crate_graph(crate_graph); | 85 | change.set_crate_graph(crate_graph); |
86 | |||
87 | let mut analysis_host = AnalysisHost::default(); | ||
88 | analysis_host.apply_change(change); | ||
89 | ServerWorldState { | ||
90 | roots_to_scan, | ||
91 | root, | ||
92 | workspaces: Arc::new(workspaces), | ||
93 | analysis_host, | ||
94 | vfs: Arc::new(RwLock::new(vfs)), | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /// Returns a vec of libraries | ||
99 | /// FIXME: better API here | ||
100 | pub fn process_changes( | ||
101 | &mut self, | ||
102 | ) -> Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)> { | ||
103 | let changes = self.vfs.write().commit_changes(); | ||
104 | if changes.is_empty() { | ||
105 | return Vec::new(); | ||
106 | } | ||
107 | let mut libs = Vec::new(); | ||
108 | let mut change = AnalysisChange::new(); | ||
109 | for c in changes { | ||
110 | log::info!("vfs change {:?}", c); | ||
111 | match c { | ||
112 | VfsChange::AddRoot { root, files } => { | ||
113 | let root_path = self.vfs.read().root2path(root); | ||
114 | if root_path.starts_with(&self.root) { | ||
115 | self.roots_to_scan -= 1; | ||
116 | for (file, path, text) in files { | ||
117 | change.add_file(SourceRootId(root.0), FileId(file.0), path, text); | ||
118 | } | ||
119 | } else { | ||
120 | let files = files | ||
121 | .into_iter() | ||
122 | .map(|(vfsfile, path, text)| (FileId(vfsfile.0), path, text)) | ||
123 | .collect(); | ||
124 | libs.push((SourceRootId(root.0), files)); | ||
125 | } | ||
126 | } | ||
127 | VfsChange::AddFile { | ||
128 | root, | ||
129 | file, | ||
130 | path, | ||
131 | text, | ||
132 | } => { | ||
133 | change.add_file(SourceRootId(root.0), FileId(file.0), path, text); | ||
134 | } | ||
135 | VfsChange::RemoveFile { root, file, path } => { | ||
136 | change.remove_file(SourceRootId(root.0), FileId(file.0), path) | ||
137 | } | ||
138 | VfsChange::ChangeFile { file, text } => { | ||
139 | change.change_file(FileId(file.0), text); | ||
140 | } | ||
141 | } | ||
142 | } | ||
176 | self.analysis_host.apply_change(change); | 143 | self.analysis_host.apply_change(change); |
144 | libs | ||
177 | } | 145 | } |
146 | |||
147 | pub fn add_lib(&mut self, data: LibraryData) { | ||
148 | self.roots_to_scan -= 1; | ||
149 | let mut change = AnalysisChange::new(); | ||
150 | change.add_library(data); | ||
151 | self.analysis_host.apply_change(change); | ||
152 | } | ||
153 | |||
178 | pub fn snapshot(&self) -> ServerWorld { | 154 | pub fn snapshot(&self) -> ServerWorld { |
179 | ServerWorld { | 155 | ServerWorld { |
180 | workspaces: Arc::clone(&self.workspaces), | 156 | workspaces: Arc::clone(&self.workspaces), |
181 | analysis: self.analysis_host.analysis(), | 157 | analysis: self.analysis_host.analysis(), |
182 | path_map: self.path_map.clone(), | 158 | vfs: Arc::clone(&self.vfs), |
183 | } | 159 | } |
184 | } | 160 | } |
185 | } | 161 | } |
@@ -193,15 +169,18 @@ impl ServerWorld { | |||
193 | let path = uri | 169 | let path = uri |
194 | .to_file_path() | 170 | .to_file_path() |
195 | .map_err(|()| format_err!("invalid uri: {}", uri))?; | 171 | .map_err(|()| format_err!("invalid uri: {}", uri))?; |
196 | self.path_map | 172 | let file = self |
197 | .get_id(&path) | 173 | .vfs |
198 | .ok_or_else(|| format_err!("unknown file: {}", path.display())) | 174 | .read() |
175 | .path2file(&path) | ||
176 | .ok_or_else(|| format_err!("unknown file: {}", path.display()))?; | ||
177 | Ok(FileId(file.0)) | ||
199 | } | 178 | } |
200 | 179 | ||
201 | pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { | 180 | pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { |
202 | let path = self.path_map.get_path(id); | 181 | let path = self.vfs.read().file2path(VfsFile(id.0)); |
203 | let url = Url::from_file_path(path) | 182 | let url = Url::from_file_path(&path) |
204 | .map_err(|()| format_err!("can't convert path to url: {}", path.display()))?; | 183 | .map_err(|_| format_err!("can't convert path to url: {}", path.display()))?; |
205 | Ok(url) | 184 | Ok(url) |
206 | } | 185 | } |
207 | } | 186 | } |