Skip to content

Commit f2775ac

Browse files
committed
reuse hover results with resultset
1 parent 70061d2 commit f2775ac

File tree

3 files changed

+173
-94
lines changed

3 files changed

+173
-94
lines changed

crates/ide/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub use crate::{
8787
references::ReferenceSearchResult,
8888
rename::RenameError,
8989
runnables::{Runnable, RunnableKind, TestId},
90-
static_index::{StaticIndex, StaticIndexedFile, TokenStaticData},
90+
static_index::{StaticIndex, StaticIndexedFile, TokenStaticData, TokenId},
9191
syntax_highlighting::{
9292
tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
9393
HlRange,

crates/ide/src/static_index.rs

Lines changed: 96 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,62 @@
11
//! This module provides `StaticIndex` which is used for powering
22
//! read-only code browsers and emitting LSIF
33
4+
use std::collections::HashMap;
5+
46
use hir::{db::HirDatabase, Crate, Module};
5-
use ide_db::base_db::{FileId, FileRange, SourceDatabaseExt};
7+
use ide_db::base_db::{FileId, SourceDatabaseExt};
68
use ide_db::RootDatabase;
9+
use ide_db::defs::Definition;
710
use rustc_hash::FxHashSet;
811
use syntax::TextRange;
912
use syntax::{AstNode, SyntaxKind::*, T};
1013

14+
use crate::hover::{get_definition_of_token, hover_for_definition};
1115
use crate::{Analysis, Cancellable, Fold, HoverConfig, HoverDocFormat, HoverResult};
1216

1317
/// A static representation of fully analyzed source code.
1418
///
1519
/// The intended use-case is powering read-only code browsers and emitting LSIF
16-
pub struct StaticIndex {
20+
pub struct StaticIndex<'a> {
1721
pub files: Vec<StaticIndexedFile>,
22+
pub tokens: TokenStore,
23+
analysis: &'a Analysis,
24+
db: &'a RootDatabase,
25+
def_map: HashMap<Definition, TokenId>,
1826
}
1927

2028
pub struct TokenStaticData {
21-
pub range: TextRange,
2229
pub hover: Option<HoverResult>,
2330
}
2431

32+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
33+
pub struct TokenId(usize);
34+
35+
#[derive(Default)]
36+
pub struct TokenStore(Vec<TokenStaticData>);
37+
38+
impl TokenStore {
39+
pub fn insert(&mut self, data: TokenStaticData) -> TokenId {
40+
let id = TokenId(self.0.len());
41+
self.0.push(data);
42+
id
43+
}
44+
45+
pub fn get(&self, id: TokenId) -> Option<&TokenStaticData> {
46+
self.0.get(id.0)
47+
}
48+
49+
pub fn iter(self) -> impl Iterator<Item=(TokenId, TokenStaticData)> {
50+
self.0.into_iter().enumerate().map(|(i, x)| {
51+
(TokenId(i), x)
52+
})
53+
}
54+
}
55+
2556
pub struct StaticIndexedFile {
2657
pub file_id: FileId,
2758
pub folds: Vec<Fold>,
28-
pub tokens: Vec<TokenStaticData>,
59+
pub tokens: Vec<(TextRange, TokenId)>,
2960
}
3061

3162
fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
@@ -41,62 +72,81 @@ fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
4172
modules
4273
}
4374

44-
impl StaticIndex {
45-
pub fn compute(db: &RootDatabase, analysis: &Analysis) -> Cancellable<StaticIndex> {
75+
impl StaticIndex<'_> {
76+
fn add_file(&mut self, file_id: FileId) -> Cancellable<()> {
77+
let folds = self.analysis.folding_ranges(file_id)?;
78+
// hovers
79+
let sema = hir::Semantics::new(self.db);
80+
let tokens_or_nodes = sema.parse(file_id).syntax().clone();
81+
let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|x| match x {
82+
syntax::NodeOrToken::Node(_) => None,
83+
syntax::NodeOrToken::Token(x) => Some(x),
84+
});
85+
let hover_config =
86+
HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) };
87+
let tokens = tokens
88+
.filter(|token| match token.kind() {
89+
IDENT
90+
| INT_NUMBER
91+
| LIFETIME_IDENT
92+
| T![self]
93+
| T![super]
94+
| T![crate] => true,
95+
_ => false,
96+
});
97+
let mut result = StaticIndexedFile {
98+
file_id,
99+
folds,
100+
tokens: vec![],
101+
};
102+
for token in tokens {
103+
let range = token.text_range();
104+
let node = token.parent().unwrap();
105+
let def = get_definition_of_token(self.db, &sema, &sema.descend_into_macros(token), file_id, range.start(), &mut None);
106+
let def = if let Some(x) = def {
107+
x
108+
} else {
109+
continue;
110+
};
111+
let id = if let Some(x) = self.def_map.get(&def) {
112+
*x
113+
} else {
114+
let x = self.tokens.insert(TokenStaticData {
115+
hover: hover_for_definition(self.db, file_id, &sema, def, node, &hover_config),
116+
});
117+
self.def_map.insert(def, x);
118+
x
119+
};
120+
result.tokens.push((range, id));
121+
}
122+
self.files.push(result);
123+
Ok(())
124+
}
125+
126+
pub fn compute<'a>(db: &'a RootDatabase, analysis: &'a Analysis) -> Cancellable<StaticIndex<'a>> {
46127
let work = all_modules(db).into_iter().filter(|module| {
47128
let file_id = module.definition_source(db).file_id.original_file(db);
48129
let source_root = db.file_source_root(file_id);
49130
let source_root = db.source_root(source_root);
50131
!source_root.is_library
51132
});
52-
133+
let mut this = StaticIndex {
134+
files: vec![],
135+
tokens: Default::default(),
136+
analysis, db,
137+
def_map: Default::default(),
138+
};
53139
let mut visited_files = FxHashSet::default();
54-
let mut result_files = Vec::<StaticIndexedFile>::new();
55140
for module in work {
56141
let file_id = module.definition_source(db).file_id.original_file(db);
57142
if visited_files.contains(&file_id) {
58143
continue;
59144
}
60-
let folds = analysis.folding_ranges(file_id)?;
61-
// hovers
62-
let sema = hir::Semantics::new(db);
63-
let tokens_or_nodes = sema.parse(file_id).syntax().clone();
64-
let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|x| match x {
65-
syntax::NodeOrToken::Node(_) => None,
66-
syntax::NodeOrToken::Token(x) => Some(x),
67-
});
68-
let hover_config =
69-
HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) };
70-
let tokens = tokens
71-
.filter(|token| match token.kind() {
72-
IDENT
73-
| INT_NUMBER
74-
| LIFETIME_IDENT
75-
| T![self]
76-
| T![super]
77-
| T![crate]
78-
| T!['(']
79-
| T![')'] => true,
80-
_ => false,
81-
})
82-
.map(|token| {
83-
let range = token.text_range();
84-
let hover = analysis
85-
.hover(
86-
&hover_config,
87-
FileRange {
88-
file_id,
89-
range: TextRange::new(range.start(), range.start()),
90-
},
91-
)?
92-
.map(|x| x.info);
93-
Ok(TokenStaticData { range, hover })
94-
})
95-
.collect::<Result<Vec<_>, _>>()?;
96-
result_files.push(StaticIndexedFile { file_id, folds, tokens });
145+
this.add_file(file_id)?;
97146
// mark the file
98147
visited_files.insert(file_id);
99148
}
100-
Ok(StaticIndex { files: result_files })
149+
//eprintln!("{:#?}", token_map);
150+
Ok(this)
101151
}
102152
}

crates/rust-analyzer/src/cli/lsif.rs

Lines changed: 76 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
//! Lsif generator
22
3+
use std::collections::HashMap;
34
use std::env;
45
use std::time::Instant;
56

6-
use ide::{StaticIndex, StaticIndexedFile, TokenStaticData};
7+
use ide::{Analysis, Cancellable, RootDatabase, StaticIndex, StaticIndexedFile, TokenId, TokenStaticData};
78
use ide_db::LineIndexDatabase;
89

910
use ide_db::base_db::salsa::{self, ParallelDatabase};
1011
use lsp_types::{lsif::*, Hover, HoverContents, NumberOrString};
1112
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
12-
use vfs::AbsPathBuf;
13+
use vfs::{AbsPathBuf, Vfs};
1314

1415
use crate::cli::{
1516
flags,
@@ -27,9 +28,12 @@ impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
2728
}
2829
}
2930

30-
#[derive(Default)]
31-
struct LsifManager {
31+
struct LsifManager<'a> {
3232
count: i32,
33+
token_map: HashMap<TokenId, Id>,
34+
analysis: &'a Analysis,
35+
db: &'a RootDatabase,
36+
vfs: &'a Vfs,
3337
}
3438

3539
#[derive(Clone, Copy)]
@@ -41,7 +45,17 @@ impl From<Id> for NumberOrString {
4145
}
4246
}
4347

44-
impl LsifManager {
48+
impl LsifManager<'_> {
49+
fn new<'a>(analysis: &'a Analysis, db: &'a RootDatabase, vfs: &'a Vfs) -> LsifManager<'a> {
50+
LsifManager {
51+
count: 0,
52+
token_map: HashMap::default(),
53+
analysis,
54+
db,
55+
vfs,
56+
}
57+
}
58+
4559
fn add(&mut self, data: Element) -> Id {
4660
let id = Id(self.count);
4761
self.emit(&serde_json::to_string(&Entry { id: id.into(), data }).unwrap());
@@ -54,33 +68,67 @@ impl LsifManager {
5468
println!("{}", data);
5569
}
5670

57-
fn add_tokens(&mut self, line_index: &LineIndex, doc_id: Id, tokens: Vec<TokenStaticData>) {
71+
fn add_token(&mut self, id: TokenId, token: TokenStaticData) {
72+
let result_set_id = self.add(Element::Vertex(Vertex::ResultSet(ResultSet { key: None })));
73+
self.token_map.insert(id, result_set_id);
74+
if let Some(hover) = token.hover {
75+
let hover_id = self.add(Element::Vertex(Vertex::HoverResult {
76+
result: Hover {
77+
contents: HoverContents::Markup(to_proto::markup_content(hover.markup)),
78+
range: None,
79+
},
80+
}));
81+
self.add(Element::Edge(Edge::Hover(EdgeData {
82+
in_v: hover_id.into(),
83+
out_v: result_set_id.into(),
84+
})));
85+
}
86+
}
87+
88+
fn add_file(&mut self, file: StaticIndexedFile) -> Cancellable<()> {
89+
let StaticIndexedFile { file_id, tokens, folds} = file;
90+
let path = self.vfs.file_path(file_id);
91+
let path = path.as_path().unwrap();
92+
let doc_id = self.add(Element::Vertex(Vertex::Document(Document {
93+
language_id: "rust".to_string(),
94+
uri: lsp_types::Url::from_file_path(path).unwrap(),
95+
})));
96+
let text = self.analysis.file_text(file_id)?;
97+
let line_index = self.db.line_index(file_id);
98+
let line_index = LineIndex {
99+
index: line_index.clone(),
100+
encoding: OffsetEncoding::Utf16,
101+
endings: LineEndings::Unix,
102+
};
103+
let result = folds
104+
.into_iter()
105+
.map(|it| to_proto::folding_range(&*text, &line_index, false, it))
106+
.collect();
107+
let folding_id = self.add(Element::Vertex(Vertex::FoldingRangeResult { result }));
108+
self.add(Element::Edge(Edge::FoldingRange(EdgeData {
109+
in_v: folding_id.into(),
110+
out_v: doc_id.into(),
111+
})));
58112
let tokens_id = tokens
59113
.into_iter()
60-
.map(|token| {
61-
let token_id = self.add(Element::Vertex(Vertex::Range {
62-
range: to_proto::range(line_index, token.range),
114+
.map(|(range, id)| {
115+
let range_id = self.add(Element::Vertex(Vertex::Range {
116+
range: to_proto::range(&line_index, range),
63117
tag: None,
64118
}));
65-
if let Some(hover) = token.hover {
66-
let hover_id = self.add(Element::Vertex(Vertex::HoverResult {
67-
result: Hover {
68-
contents: HoverContents::Markup(to_proto::markup_content(hover.markup)),
69-
range: None,
70-
},
71-
}));
72-
self.add(Element::Edge(Edge::Hover(EdgeData {
73-
in_v: hover_id.into(),
74-
out_v: token_id.into(),
75-
})));
76-
}
77-
token_id.into()
119+
let result_set_id = *self.token_map.get(&id).expect("token map doesn't contain id");
120+
self.add(Element::Edge(Edge::Next(EdgeData {
121+
in_v: result_set_id.into(),
122+
out_v: range_id.into(),
123+
})));
124+
range_id.into()
78125
})
79126
.collect();
80127
self.add(Element::Edge(Edge::Contains(EdgeDataMultiIn {
81128
in_vs: tokens_id,
82129
out_v: doc_id.into(),
83130
})));
131+
Ok(())
84132
}
85133
}
86134

@@ -106,37 +154,18 @@ impl flags::Lsif {
106154

107155
let si = StaticIndex::compute(db, &analysis)?;
108156

109-
let mut lsif = LsifManager::default();
157+
let mut lsif = LsifManager::new(&analysis, db, &vfs);
110158
lsif.add(Element::Vertex(Vertex::MetaData(MetaData {
111159
version: String::from("0.5.0"),
112160
project_root: lsp_types::Url::from_file_path(path).unwrap(),
113161
position_encoding: Encoding::Utf16,
114162
tool_info: None,
115163
})));
116-
for StaticIndexedFile { file_id, folds, tokens } in si.files {
117-
let path = vfs.file_path(file_id);
118-
let path = path.as_path().unwrap();
119-
let doc_id = lsif.add(Element::Vertex(Vertex::Document(Document {
120-
language_id: "rust".to_string(),
121-
uri: lsp_types::Url::from_file_path(path).unwrap(),
122-
})));
123-
let text = analysis.file_text(file_id)?;
124-
let line_index = db.line_index(file_id);
125-
let line_index = LineIndex {
126-
index: line_index.clone(),
127-
encoding: OffsetEncoding::Utf16,
128-
endings: LineEndings::Unix,
129-
};
130-
let result = folds
131-
.into_iter()
132-
.map(|it| to_proto::folding_range(&*text, &line_index, false, it))
133-
.collect();
134-
let folding_id = lsif.add(Element::Vertex(Vertex::FoldingRangeResult { result }));
135-
lsif.add(Element::Edge(Edge::FoldingRange(EdgeData {
136-
in_v: folding_id.into(),
137-
out_v: doc_id.into(),
138-
})));
139-
lsif.add_tokens(&line_index, doc_id, tokens);
164+
for (id, token) in si.tokens.iter() {
165+
lsif.add_token(id, token);
166+
}
167+
for file in si.files {
168+
lsif.add_file(file)?;
140169
}
141170
eprintln!("Generating LSIF finished in {:?}", now.elapsed());
142171
Ok(())

0 commit comments

Comments
 (0)