1 //! LSIF (language server index format) generator 2 3 use std::collections::HashMap; 4 use std::env; 5 use std::time::Instant; 6 7 use ide::{ 8 Analysis, FileId, FileRange, MonikerKind, PackageInformation, RootDatabase, StaticIndex, 9 StaticIndexedFile, TokenId, TokenStaticData, 10 }; 11 use ide_db::LineIndexDatabase; 12 13 use ide_db::base_db::salsa::{self, ParallelDatabase}; 14 use ide_db::line_index::WideEncoding; 15 use lsp_types::{self, lsif}; 16 use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource}; 17 use vfs::{AbsPathBuf, Vfs}; 18 19 use crate::cli::load_cargo::ProcMacroServerChoice; 20 use crate::cli::{ 21 flags, 22 load_cargo::{load_workspace, LoadCargoConfig}, 23 Result, 24 }; 25 use crate::line_index::{LineEndings, LineIndex, PositionEncoding}; 26 use crate::to_proto; 27 use crate::version::version; 28 29 /// Need to wrap Snapshot to provide `Clone` impl for `map_with` 30 struct Snap<DB>(DB); 31 impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> { clone(&self) -> Snap<salsa::Snapshot<DB>>32 fn clone(&self) -> Snap<salsa::Snapshot<DB>> { 33 Snap(self.0.snapshot()) 34 } 35 } 36 37 struct LsifManager<'a> { 38 count: i32, 39 token_map: HashMap<TokenId, Id>, 40 range_map: HashMap<FileRange, Id>, 41 file_map: HashMap<FileId, Id>, 42 package_map: HashMap<PackageInformation, Id>, 43 analysis: &'a Analysis, 44 db: &'a RootDatabase, 45 vfs: &'a Vfs, 46 } 47 48 #[derive(Clone, Copy)] 49 struct Id(i32); 50 51 impl From<Id> for lsp_types::NumberOrString { from(Id(x): Id) -> Self52 fn from(Id(x): Id) -> Self { 53 lsp_types::NumberOrString::Number(x) 54 } 55 } 56 57 impl LsifManager<'_> { new<'a>(analysis: &'a Analysis, db: &'a RootDatabase, vfs: &'a Vfs) -> LsifManager<'a>58 fn new<'a>(analysis: &'a Analysis, db: &'a RootDatabase, vfs: &'a Vfs) -> LsifManager<'a> { 59 LsifManager { 60 count: 0, 61 token_map: HashMap::default(), 62 range_map: HashMap::default(), 63 file_map: HashMap::default(), 64 package_map: HashMap::default(), 65 analysis, 66 db, 67 vfs, 68 } 69 } 70 add(&mut self, data: lsif::Element) -> Id71 fn add(&mut self, data: lsif::Element) -> Id { 72 let id = Id(self.count); 73 self.emit(&serde_json::to_string(&lsif::Entry { id: id.into(), data }).unwrap()); 74 self.count += 1; 75 id 76 } 77 add_vertex(&mut self, vertex: lsif::Vertex) -> Id78 fn add_vertex(&mut self, vertex: lsif::Vertex) -> Id { 79 self.add(lsif::Element::Vertex(vertex)) 80 } 81 add_edge(&mut self, edge: lsif::Edge) -> Id82 fn add_edge(&mut self, edge: lsif::Edge) -> Id { 83 self.add(lsif::Element::Edge(edge)) 84 } 85 86 // FIXME: support file in addition to stdout here emit(&self, data: &str)87 fn emit(&self, data: &str) { 88 println!("{data}"); 89 } 90 get_token_id(&mut self, id: TokenId) -> Id91 fn get_token_id(&mut self, id: TokenId) -> Id { 92 if let Some(x) = self.token_map.get(&id) { 93 return *x; 94 } 95 let result_set_id = self.add_vertex(lsif::Vertex::ResultSet(lsif::ResultSet { key: None })); 96 self.token_map.insert(id, result_set_id); 97 result_set_id 98 } 99 get_package_id(&mut self, package_information: PackageInformation) -> Id100 fn get_package_id(&mut self, package_information: PackageInformation) -> Id { 101 if let Some(x) = self.package_map.get(&package_information) { 102 return *x; 103 } 104 let pi = package_information.clone(); 105 let result_set_id = 106 self.add_vertex(lsif::Vertex::PackageInformation(lsif::PackageInformation { 107 name: pi.name, 108 manager: "cargo".to_string(), 109 uri: None, 110 content: None, 111 repository: pi.repo.map(|url| lsif::Repository { 112 url, 113 r#type: "git".to_string(), 114 commit_id: None, 115 }), 116 version: pi.version, 117 })); 118 self.package_map.insert(package_information, result_set_id); 119 result_set_id 120 } 121 get_range_id(&mut self, id: FileRange) -> Id122 fn get_range_id(&mut self, id: FileRange) -> Id { 123 if let Some(x) = self.range_map.get(&id) { 124 return *x; 125 } 126 let file_id = id.file_id; 127 let doc_id = self.get_file_id(file_id); 128 let line_index = self.db.line_index(file_id); 129 let line_index = LineIndex { 130 index: line_index, 131 encoding: PositionEncoding::Wide(WideEncoding::Utf16), 132 endings: LineEndings::Unix, 133 }; 134 let range_id = self.add_vertex(lsif::Vertex::Range { 135 range: to_proto::range(&line_index, id.range), 136 tag: None, 137 }); 138 self.add_edge(lsif::Edge::Contains(lsif::EdgeDataMultiIn { 139 in_vs: vec![range_id.into()], 140 out_v: doc_id.into(), 141 })); 142 range_id 143 } 144 get_file_id(&mut self, id: FileId) -> Id145 fn get_file_id(&mut self, id: FileId) -> Id { 146 if let Some(x) = self.file_map.get(&id) { 147 return *x; 148 } 149 let path = self.vfs.file_path(id); 150 let path = path.as_path().unwrap(); 151 let doc_id = self.add_vertex(lsif::Vertex::Document(lsif::Document { 152 language_id: "rust".to_string(), 153 uri: lsp_types::Url::from_file_path(path).unwrap(), 154 })); 155 self.file_map.insert(id, doc_id); 156 doc_id 157 } 158 add_token(&mut self, id: TokenId, token: TokenStaticData)159 fn add_token(&mut self, id: TokenId, token: TokenStaticData) { 160 let result_set_id = self.get_token_id(id); 161 if let Some(hover) = token.hover { 162 let hover_id = self.add_vertex(lsif::Vertex::HoverResult { 163 result: lsp_types::Hover { 164 contents: lsp_types::HoverContents::Markup(to_proto::markup_content( 165 hover.markup, 166 ide::HoverDocFormat::Markdown, 167 )), 168 range: None, 169 }, 170 }); 171 self.add_edge(lsif::Edge::Hover(lsif::EdgeData { 172 in_v: hover_id.into(), 173 out_v: result_set_id.into(), 174 })); 175 } 176 if let Some(moniker) = token.moniker { 177 let package_id = self.get_package_id(moniker.package_information); 178 let moniker_id = self.add_vertex(lsif::Vertex::Moniker(lsp_types::Moniker { 179 scheme: "rust-analyzer".to_string(), 180 identifier: moniker.identifier.to_string(), 181 unique: lsp_types::UniquenessLevel::Scheme, 182 kind: Some(match moniker.kind { 183 MonikerKind::Import => lsp_types::MonikerKind::Import, 184 MonikerKind::Export => lsp_types::MonikerKind::Export, 185 }), 186 })); 187 self.add_edge(lsif::Edge::PackageInformation(lsif::EdgeData { 188 in_v: package_id.into(), 189 out_v: moniker_id.into(), 190 })); 191 self.add_edge(lsif::Edge::Moniker(lsif::EdgeData { 192 in_v: moniker_id.into(), 193 out_v: result_set_id.into(), 194 })); 195 } 196 if let Some(def) = token.definition { 197 let result_id = self.add_vertex(lsif::Vertex::DefinitionResult); 198 let def_vertex = self.get_range_id(def); 199 self.add_edge(lsif::Edge::Item(lsif::Item { 200 document: (*self.file_map.get(&def.file_id).unwrap()).into(), 201 property: None, 202 edge_data: lsif::EdgeDataMultiIn { 203 in_vs: vec![def_vertex.into()], 204 out_v: result_id.into(), 205 }, 206 })); 207 self.add_edge(lsif::Edge::Definition(lsif::EdgeData { 208 in_v: result_id.into(), 209 out_v: result_set_id.into(), 210 })); 211 } 212 if !token.references.is_empty() { 213 let result_id = self.add_vertex(lsif::Vertex::ReferenceResult); 214 self.add_edge(lsif::Edge::References(lsif::EdgeData { 215 in_v: result_id.into(), 216 out_v: result_set_id.into(), 217 })); 218 let mut edges = token.references.iter().fold( 219 HashMap::<_, Vec<lsp_types::NumberOrString>>::new(), 220 |mut edges, x| { 221 let entry = 222 edges.entry((x.range.file_id, x.is_definition)).or_insert_with(Vec::new); 223 entry.push((*self.range_map.get(&x.range).unwrap()).into()); 224 edges 225 }, 226 ); 227 for x in token.references { 228 if let Some(vertices) = edges.remove(&(x.range.file_id, x.is_definition)) { 229 self.add_edge(lsif::Edge::Item(lsif::Item { 230 document: (*self.file_map.get(&x.range.file_id).unwrap()).into(), 231 property: Some(if x.is_definition { 232 lsif::ItemKind::Definitions 233 } else { 234 lsif::ItemKind::References 235 }), 236 edge_data: lsif::EdgeDataMultiIn { 237 in_vs: vertices, 238 out_v: result_id.into(), 239 }, 240 })); 241 } 242 } 243 } 244 } 245 add_file(&mut self, file: StaticIndexedFile)246 fn add_file(&mut self, file: StaticIndexedFile) { 247 let StaticIndexedFile { file_id, tokens, folds, .. } = file; 248 let doc_id = self.get_file_id(file_id); 249 let text = self.analysis.file_text(file_id).unwrap(); 250 let line_index = self.db.line_index(file_id); 251 let line_index = LineIndex { 252 index: line_index, 253 encoding: PositionEncoding::Wide(WideEncoding::Utf16), 254 endings: LineEndings::Unix, 255 }; 256 let result = folds 257 .into_iter() 258 .map(|it| to_proto::folding_range(&text, &line_index, false, it)) 259 .collect(); 260 let folding_id = self.add_vertex(lsif::Vertex::FoldingRangeResult { result }); 261 self.add_edge(lsif::Edge::FoldingRange(lsif::EdgeData { 262 in_v: folding_id.into(), 263 out_v: doc_id.into(), 264 })); 265 let tokens_id = tokens 266 .into_iter() 267 .map(|(range, id)| { 268 let range_id = self.add_vertex(lsif::Vertex::Range { 269 range: to_proto::range(&line_index, range), 270 tag: None, 271 }); 272 self.range_map.insert(FileRange { file_id, range }, range_id); 273 let result_set_id = self.get_token_id(id); 274 self.add_edge(lsif::Edge::Next(lsif::EdgeData { 275 in_v: result_set_id.into(), 276 out_v: range_id.into(), 277 })); 278 range_id.into() 279 }) 280 .collect(); 281 self.add_edge(lsif::Edge::Contains(lsif::EdgeDataMultiIn { 282 in_vs: tokens_id, 283 out_v: doc_id.into(), 284 })); 285 } 286 } 287 288 impl flags::Lsif { run(self) -> Result<()>289 pub fn run(self) -> Result<()> { 290 eprintln!("Generating LSIF started..."); 291 let now = Instant::now(); 292 let mut cargo_config = CargoConfig::default(); 293 cargo_config.sysroot = Some(RustLibSource::Discover); 294 let no_progress = &|_| (); 295 let load_cargo_config = LoadCargoConfig { 296 load_out_dirs_from_check: true, 297 with_proc_macro_server: ProcMacroServerChoice::Sysroot, 298 prefill_caches: false, 299 }; 300 let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path)); 301 let manifest = ProjectManifest::discover_single(&path)?; 302 303 let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; 304 305 let (host, vfs, _proc_macro) = 306 load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?; 307 let db = host.raw_database(); 308 let analysis = host.analysis(); 309 310 let si = StaticIndex::compute(&analysis); 311 312 let mut lsif = LsifManager::new(&analysis, db, &vfs); 313 lsif.add_vertex(lsif::Vertex::MetaData(lsif::MetaData { 314 version: String::from("0.5.0"), 315 project_root: lsp_types::Url::from_file_path(path).unwrap(), 316 position_encoding: lsif::Encoding::Utf16, 317 tool_info: Some(lsp_types::lsif::ToolInfo { 318 name: "rust-analyzer".to_string(), 319 args: vec![], 320 version: Some(version().to_string()), 321 }), 322 })); 323 for file in si.files { 324 lsif.add_file(file); 325 } 326 for (id, token) in si.tokens.iter() { 327 lsif.add_token(id, token); 328 } 329 eprintln!("Generating LSIF finished in {:?}", now.elapsed()); 330 Ok(()) 331 } 332 } 333