7 use rustc_hash::FxHashMap;
8 use languageserver_types::Url;
9 use ra_analysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId, LibraryData, FileResolver};
13 path_map::{PathMap, Root},
14 vfs::{FileEvent, FileEventKind},
15 project_model::CargoWorkspace,
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>>,
26 pub struct ServerWorld {
27 pub workspaces: Arc<Vec<CargoWorkspace>>,
28 pub analysis: Analysis,
29 pub path_map: PathMap,
32 impl ServerWorldState {
33 pub fn new() -> ServerWorldState {
35 workspaces: Arc::new(Vec::new()),
36 analysis_host: AnalysisHost::new(),
37 path_map: PathMap::new(),
38 mem_map: FxHashMap::default(),
41 pub fn apply_fs_changes(&mut self, events: Vec<FileEvent>) {
43 let pm = &mut self.path_map;
44 let mm = &mut self.mem_map;
45 let changes = events.into_iter()
47 let text = match event.kind {
48 FileEventKind::Add(text) => Some(text),
53 (pm.get_or_insert(path, Root::Workspace), text)
55 .filter_map(|(id, text)| {
56 if mm.contains_key(&id) {
63 self.analysis_host.change_files(changes);
65 self.analysis_host.set_file_resolver(Arc::new(self.path_map.clone()));
67 pub fn events_to_files(&mut self, events: Vec<FileEvent>) -> (Vec<(FileId, String)>, Arc<FileResolver>) {
69 let pm = &mut self.path_map;
72 let text = match event.kind {
73 FileEventKind::Add(text) => text,
77 .map(|(path, text)| (pm.get_or_insert(path, Root::Lib), text))
80 let resolver = Arc::new(self.path_map.clone());
83 pub fn add_lib(&mut self, data: LibraryData) {
84 self.analysis_host.add_library(data);
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));
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())
101 if self.path_map.get_root(file_id) != Root::Lib {
102 self.analysis_host.change_file(file_id, Some(text));
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())
111 match self.mem_map.remove(&file_id) {
113 None => bail!("unmatched close notification"),
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);
122 pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) {
123 let mut crate_roots = FxHashMap::default();
127 .flat_map(move |pkg| pkg.targets(ws))
128 .map(move |tgt| tgt.root(ws))
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);
136 let crate_graph = CrateGraph { crate_roots };
137 self.workspaces = Arc::new(ws);
138 self.analysis_host.set_crate_graph(crate_graph);
140 pub fn snapshot(&self) -> ServerWorld {
142 workspaces: Arc::clone(&self.workspaces),
143 analysis: self.analysis_host.analysis(),
144 path_map: self.path_map.clone()
150 pub fn analysis(&self) -> &Analysis {
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()))
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()))?;