• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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