7 use languageserver_types::Url;
9 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FileResolver, LibraryData,
11 use rustc_hash::FxHashMap;
12 use failure::{bail, format_err};
15 path_map::{PathMap, Root},
16 project_model::{CargoWorkspace, TargetKind},
17 vfs::{FileEvent, FileEventKind},
21 #[derive(Debug, Default)]
22 pub struct ServerWorldState {
23 pub workspaces: Arc<Vec<CargoWorkspace>>,
24 pub analysis_host: AnalysisHost,
25 pub path_map: PathMap,
26 pub mem_map: FxHashMap<FileId, Option<String>>,
29 pub struct ServerWorld {
30 pub workspaces: Arc<Vec<CargoWorkspace>>,
31 pub analysis: Analysis,
32 pub path_map: PathMap,
35 impl ServerWorldState {
36 pub fn apply_fs_changes(&mut self, events: Vec<FileEvent>) {
37 let mut change = AnalysisChange::new();
38 let mut inserted = false;
40 let pm = &mut self.path_map;
41 let mm = &mut self.mem_map;
45 let text = match event.kind {
46 FileEventKind::Add(text) => text,
51 let (ins, file_id) = pm.get_or_insert(path, Root::Workspace);
55 .filter_map(|(file_id, text)| {
56 if mm.contains_key(&file_id) {
57 mm.insert(file_id, Some(text));
63 .for_each(|(file_id, text)| change.add_file(file_id, text));
66 change.set_file_resolver(Arc::new(self.path_map.clone()))
68 self.analysis_host.apply_change(change);
70 pub fn events_to_files(
72 events: Vec<FileEvent>,
73 ) -> (Vec<(FileId, String)>, Arc<FileResolver>) {
75 let pm = &mut self.path_map;
79 let FileEventKind::Add(text) = event.kind;
82 .map(|(path, text)| (pm.get_or_insert(path, Root::Lib).1, text))
85 let resolver = Arc::new(self.path_map.clone());
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);
94 pub fn add_mem_file(&mut self, path: PathBuf, text: String) -> FileId {
95 let (inserted, file_id) = self.path_map.get_or_insert(path, Root::Workspace);
96 if self.path_map.get_root(file_id) != Root::Lib {
97 let mut change = AnalysisChange::new();
99 change.add_file(file_id, text);
100 change.set_file_resolver(Arc::new(self.path_map.clone()));
102 change.change_file(file_id, text);
104 self.analysis_host.apply_change(change);
106 self.mem_map.insert(file_id, None);
110 pub fn change_mem_file(&mut self, path: &Path, text: String) -> Result<()> {
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);
123 pub fn remove_mem_file(&mut self, path: &Path) -> Result<FileId> {
127 .ok_or_else(|| format_err!("change to unknown file: {}", path.display()))?;
128 match self.mem_map.remove(&file_id) {
130 None => bail!("unmatched close notification"),
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);
139 self.analysis_host.apply_change(change);
143 pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) {
144 let mut crate_graph = CrateGraph::default();
145 let mut pkg_to_lib_crate = FxHashMap::default();
146 let mut pkg_crates = FxHashMap::default();
147 for ws in ws.iter() {
148 for pkg in ws.packages() {
149 for tgt in pkg.targets(ws) {
150 let root = tgt.root(ws);
151 if let Some(file_id) = self.path_map.get_id(root) {
152 let crate_id = crate_graph.add_crate_root(file_id);
153 if tgt.kind(ws) == TargetKind::Lib {
154 pkg_to_lib_crate.insert(pkg, crate_id);
158 .or_insert_with(Vec::new)
163 for pkg in ws.packages() {
164 for dep in pkg.dependencies(ws) {
165 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
166 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
167 crate_graph.add_dep(from, dep.name.clone(), to);
173 self.workspaces = Arc::new(ws);
174 let mut change = AnalysisChange::new();
175 change.set_crate_graph(crate_graph);
176 self.analysis_host.apply_change(change);
178 pub fn snapshot(&self) -> ServerWorld {
180 workspaces: Arc::clone(&self.workspaces),
181 analysis: self.analysis_host.analysis(),
182 path_map: self.path_map.clone(),
188 pub fn analysis(&self) -> &Analysis {
192 pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
195 .map_err(|()| format_err!("invalid uri: {}", uri))?;
198 .ok_or_else(|| format_err!("unknown file: {}", path.display()))
201 pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
202 let path = self.path_map.get_path(id);
203 let url = Url::from_file_path(path)
204 .map_err(|()| format_err!("can't convert path to url: {}", path.display()))?;