Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit 337cd40

Browse files
committed
Auto merge of #1538 - nrc:analysis-idents, r=Xanewok
rls-analysis: add a feature to return all identifiers in a span. This is useful for getting all symbols in a file, for highlighting identifiers on a line or highlighted region, etc. I think it could get a little expensive, so I made it opt-in for now. r? @Xanewok
2 parents b06cbba + 7db736d commit 337cd40

File tree

4 files changed

+140
-17
lines changed

4 files changed

+140
-17
lines changed

rls-analysis/Cargo.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,15 @@ exclude = [
1111
"test_data/*",
1212
]
1313

14+
[features]
15+
default = []
16+
idents = ["rls-span/nightly"]
17+
derive = ["rls-data/derive", "rls-span/derive"]
18+
1419
[dependencies]
1520
log = "0.4"
1621
rls-data = "= 0.19"
17-
rls-span = "0.5"
22+
rls-span = "0.5.2"
1823
derive-new = "0.5"
1924
fst = { version = "0.3", default-features = false }
2025
itertools = "0.8"
@@ -25,7 +30,3 @@ serde_json = "1.0"
2530
[dev-dependencies]
2631
lazy_static = "1"
2732
env_logger = "0.5"
28-
29-
[features]
30-
default = []
31-
derive = ["rls-data/derive", "rls-span/derive"]

rls-analysis/src/analysis.rs

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use fst;
2-
use std::collections::{HashMap, HashSet};
2+
use std::collections::{BTreeMap, HashMap, HashSet};
33
use std::iter;
44
use std::path::{Path, PathBuf};
55
use std::time::SystemTime;
66

77
use crate::raw::{CrateId, DefKind};
88
use crate::{Id, Span, SymbolQuery};
9+
use span::{Column, Row, ZeroIndexed};
910

1011
/// This is the main database that contains all the collected symbol information,
1112
/// such as definitions, their mapping between spans, hierarchy and so on,
1213
/// organized in a per-crate fashion.
13-
#[derive(Debug)]
1414
pub(crate) struct Analysis {
1515
/// Contains lowered data with global inter-crate `Id`s per each crate.
1616
pub per_crate: HashMap<CrateId, PerCrateAnalysis>,
@@ -31,7 +31,6 @@ pub(crate) struct Analysis {
3131
pub src_url_base: String,
3232
}
3333

34-
#[derive(Debug)]
3534
pub struct PerCrateAnalysis {
3635
// Map span to id of def (either because it is the span of the def, or of
3736
// the def for the ref).
@@ -49,6 +48,7 @@ pub struct PerCrateAnalysis {
4948
pub ref_spans: HashMap<Id, Vec<Span>>,
5049
pub globs: HashMap<Span, Glob>,
5150
pub impls: HashMap<Id, Vec<Span>>,
51+
pub idents: HashMap<PathBuf, IdentsByLine>,
5252

5353
pub root_id: Option<Id>,
5454
pub timestamp: SystemTime,
@@ -103,6 +103,40 @@ pub struct Def {
103103
// pub sig: Option<Signature>,
104104
}
105105

106+
pub type Idents = HashMap<PathBuf, IdentsByLine>;
107+
pub type IdentsByLine = BTreeMap<Row<ZeroIndexed>, IdentsByColumn>;
108+
pub type IdentsByColumn = BTreeMap<Column<ZeroIndexed>, IdentBound>;
109+
110+
/// We store the identifiers for a file in a BTreeMap ordered by starting index.
111+
/// This struct contains the rest of the information we need to create an `Ident`.
112+
///
113+
/// We're optimising for space, rather than speed (of getting an Ident), because
114+
/// we have to build the whole index for every file (which is a lot for a large
115+
/// project), whereas we only get idents a few at a time and not very often.
116+
#[derive(new, Clone, Debug)]
117+
pub struct IdentBound {
118+
pub column_end: Column<ZeroIndexed>,
119+
pub id: Id,
120+
pub kind: IdentKind,
121+
}
122+
123+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
124+
pub enum IdentKind {
125+
Def,
126+
Ref,
127+
}
128+
129+
/// An identifier (either a reference or definition).
130+
///
131+
/// This struct represents the syntactic name, use the `id` to look up semantic
132+
/// information.
133+
#[derive(new, Clone, Debug)]
134+
pub struct Ident {
135+
pub span: Span,
136+
pub id: Id,
137+
pub kind: IdentKind,
138+
}
139+
106140
#[derive(Debug, Clone)]
107141
pub struct Signature {
108142
pub span: Span,
@@ -139,6 +173,7 @@ impl PerCrateAnalysis {
139173
ref_spans: HashMap::new(),
140174
globs: HashMap::new(),
141175
impls: HashMap::new(),
176+
idents: HashMap::new(),
142177
root_id: None,
143178
timestamp,
144179
path,
@@ -155,6 +190,48 @@ impl PerCrateAnalysis {
155190
None => false,
156191
}
157192
}
193+
194+
// Returns all identifiers which overlap with `span`. There is no guarantee about
195+
// the ordering of identifiers in the result, but they will probably be roughly
196+
// in order of appearance.
197+
#[cfg(feature = "idents")]
198+
fn idents(&self, span: &Span) -> Vec<Ident> {
199+
self.idents
200+
.get(&span.file)
201+
.map(|by_line| {
202+
(span.range.row_start..=span.range.row_end)
203+
.flat_map(|line| {
204+
let vec = by_line
205+
.get(&line)
206+
.iter()
207+
.flat_map(|by_col| {
208+
by_col.into_iter().filter_map(|(col_start, id)| {
209+
if col_start <= &span.range.col_end
210+
&& id.column_end >= span.range.col_start
211+
{
212+
Some(Ident::new(
213+
Span::new(
214+
line,
215+
line,
216+
*col_start,
217+
id.column_end,
218+
span.file.clone(),
219+
),
220+
id.id,
221+
id.kind,
222+
))
223+
} else {
224+
None
225+
}
226+
})
227+
})
228+
.collect::<Vec<Ident>>();
229+
vec.into_iter()
230+
})
231+
.collect::<Vec<Ident>>()
232+
})
233+
.unwrap_or_else(Vec::new)
234+
}
158235
}
159236

160237
impl Analysis {
@@ -302,6 +379,19 @@ impl Analysis {
302379
self.for_each_crate(|c| c.defs_per_file.get(file).map(&f))
303380
}
304381

382+
#[cfg(feature = "idents")]
383+
pub fn idents(&self, span: &Span) -> Vec<Ident> {
384+
self.for_each_crate(|c| {
385+
let result = c.idents(span);
386+
if result.is_empty() {
387+
None
388+
} else {
389+
Some(result)
390+
}
391+
})
392+
.unwrap_or_else(Vec::new)
393+
}
394+
305395
pub fn query_defs(&self, query: SymbolQuery) -> Vec<Def> {
306396
let mut crates = Vec::with_capacity(self.per_crate.len());
307397
let stream = query.build_stream(self.per_crate.values().map(|c| {

rls-analysis/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ mod test;
1919
mod util;
2020

2121
use analysis::Analysis;
22-
pub use analysis::{Def, Ref};
22+
pub use analysis::{Def, Ident, IdentKind, Ref};
2323
pub use loader::{AnalysisLoader, CargoAnalysisLoader, SearchDirectory, Target};
2424
pub use raw::{name_space_for_def_kind, read_analysis_from_files, Crate, CrateId, DefKind};
2525
pub use symbol_query::SymbolQuery;
@@ -31,7 +31,6 @@ use std::sync::Mutex;
3131
use std::time::{Instant, SystemTime};
3232
use std::u64;
3333

34-
#[derive(Debug)]
3534
pub struct AnalysisHost<L: AnalysisLoader = CargoAnalysisLoader> {
3635
analysis: Mutex<Option<Analysis>>,
3736
master_crate_map: Mutex<HashMap<CrateId, u32>>,
@@ -427,6 +426,12 @@ impl<L: AnalysisLoader> AnalysisHost<L> {
427426
self.with_analysis(|a| Some(a.with_def_names(name, Clone::clone)))
428427
}
429428

429+
/// Returns all identifiers which overlap the given span.
430+
#[cfg(feature = "idents")]
431+
pub fn idents(&self, span: &Span) -> AResult<Vec<Ident>> {
432+
self.with_analysis(|a| Some(a.idents(span)))
433+
}
434+
430435
pub fn symbols(&self, file_name: &Path) -> AResult<Vec<SymbolResult>> {
431436
self.with_analysis(|a| {
432437
a.with_defs_per_file(file_name, |ids| {

rls-analysis/src/lowering.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
//! in-memory representation.
33
44
use crate::analysis::{Def, Glob, PerCrateAnalysis, Ref};
5+
#[cfg(feature = "idents")]
6+
use crate::analysis::{IdentBound, IdentKind, IdentsByColumn, IdentsByLine};
57
use crate::loader::AnalysisLoader;
68
use crate::raw::{self, CrateId, DefKind, RelationKind};
79
use crate::util;
@@ -186,7 +188,7 @@ impl<'a> CrateReader<'a> {
186188
.unwrap()
187189
.crate_names
188190
.entry(krate.id.name.clone())
189-
.or_insert_with(|| vec![])
191+
.or_insert_with(Vec::new)
190192
.push(krate.id.clone());
191193
}
192194

@@ -242,10 +244,30 @@ impl<'a> CrateReader<'a> {
242244
ve.insert(Ref::Id(def_id));
243245
}
244246
}
245-
analysis.ref_spans.entry(def_id).or_insert_with(|| vec![]).push(span);
247+
248+
#[cfg(feature = "idents")]
249+
{
250+
Self::record_ident(analysis, &span, def_id, IdentKind::Ref);
251+
}
252+
analysis.ref_spans.entry(def_id).or_insert_with(Vec::new).push(span);
246253
}
247254
}
248255

256+
#[cfg(feature = "idents")]
257+
fn record_ident(analysis: &mut PerCrateAnalysis, span: &Span, id: Id, kind: IdentKind) {
258+
let row_start = span.range.row_start;
259+
let col_start = span.range.col_start;
260+
let col_end = span.range.col_end;
261+
analysis
262+
.idents
263+
.entry(span.file.clone())
264+
.or_insert_with(IdentsByLine::new)
265+
.entry(row_start)
266+
.or_insert_with(IdentsByColumn::new)
267+
.entry(col_start)
268+
.or_insert_with(|| IdentBound::new(col_end, id, kind));
269+
}
270+
249271
// We are sometimes asked to analyze the same crate twice. This can happen due to duplicate data,
250272
// but more frequently is due to compiling it twice with different Cargo targets (e.g., bin and test).
251273
// In that case there will be two crates with the same names, but different disambiguators. We
@@ -314,14 +336,14 @@ impl<'a> CrateReader<'a> {
314336
let id = self.id_from_compiler_id(d.id);
315337
if id != NULL && !analysis.defs.contains_key(&id) {
316338
let file_name = span.file.clone();
317-
analysis.defs_per_file.entry(file_name).or_insert_with(|| vec![]).push(id);
339+
analysis.defs_per_file.entry(file_name).or_insert_with(Vec::new).push(id);
318340
let decl_id = match d.decl_id {
319341
Some(ref decl_id) => {
320342
let def_id = self.id_from_compiler_id(*decl_id);
321343
analysis
322344
.ref_spans
323345
.entry(def_id)
324-
.or_insert_with(|| vec![])
346+
.or_insert_with(Vec::new)
325347
.push(span.clone());
326348
Ref::Id(def_id)
327349
}
@@ -336,7 +358,7 @@ impl<'a> CrateReader<'a> {
336358
}
337359
}
338360

339-
analysis.def_names.entry(d.name.clone()).or_insert_with(|| vec![]).push(id);
361+
analysis.def_names.entry(d.name.clone()).or_insert_with(Vec::new).push(id);
340362

341363
// NOTE not every Def will have a name, e.g. test_data/hello/src/main is analyzed with an implicit module
342364
// that's fine, but no need to index in def_trie
@@ -355,6 +377,11 @@ impl<'a> CrateReader<'a> {
355377
.extend(d.children.iter().map(|id| self.id_from_compiler_id(*id)));
356378
}
357379

380+
#[cfg(feature = "idents")]
381+
{
382+
Self::record_ident(analysis, &span, id, IdentKind::Def);
383+
}
384+
358385
let def = Def {
359386
kind: d.kind,
360387
span,
@@ -431,13 +458,13 @@ impl<'a> CrateReader<'a> {
431458
if self_id != NULL {
432459
if let Some(self_id) = abs_ref_id(self_id, analysis, project_analysis) {
433460
trace!("record impl for self type {:?} {}", span, self_id);
434-
analysis.impls.entry(self_id).or_insert_with(|| vec![]).push(span.clone());
461+
analysis.impls.entry(self_id).or_insert_with(Vec::new).push(span.clone());
435462
}
436463
}
437464
if trait_id != NULL {
438465
if let Some(trait_id) = abs_ref_id(trait_id, analysis, project_analysis) {
439466
trace!("record impl for trait {:?} {}", span, trait_id);
440-
analysis.impls.entry(trait_id).or_insert_with(|| vec![]).push(span);
467+
analysis.impls.entry(trait_id).or_insert_with(Vec::new).push(span);
441468
}
442469
}
443470
}

0 commit comments

Comments
 (0)