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