diff options
Diffstat (limited to 'crates/ra_ide/src/references/search_scope.rs')
-rw-r--r-- | crates/ra_ide/src/references/search_scope.rs | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs new file mode 100644 index 000000000..f5c9589f4 --- /dev/null +++ b/crates/ra_ide/src/references/search_scope.rs | |||
@@ -0,0 +1,145 @@ | |||
1 | //! Generally, `search_scope` returns files that might contain references for the element. | ||
2 | //! For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates. | ||
3 | //! In some cases, the location of the references is known to within a `TextRange`, | ||
4 | //! e.g. for things like local variables. | ||
5 | use std::mem; | ||
6 | |||
7 | use hir::{DefWithBody, HasSource, ModuleSource}; | ||
8 | use ra_db::{FileId, SourceDatabase, SourceDatabaseExt}; | ||
9 | use ra_prof::profile; | ||
10 | use ra_syntax::{AstNode, TextRange}; | ||
11 | use rustc_hash::FxHashMap; | ||
12 | |||
13 | use crate::db::RootDatabase; | ||
14 | |||
15 | use super::{NameDefinition, NameKind}; | ||
16 | |||
17 | pub struct SearchScope { | ||
18 | entries: FxHashMap<FileId, Option<TextRange>>, | ||
19 | } | ||
20 | |||
21 | impl SearchScope { | ||
22 | fn new(entries: FxHashMap<FileId, Option<TextRange>>) -> SearchScope { | ||
23 | SearchScope { entries } | ||
24 | } | ||
25 | pub fn single_file(file: FileId) -> SearchScope { | ||
26 | SearchScope::new(std::iter::once((file, None)).collect()) | ||
27 | } | ||
28 | pub(crate) fn intersection(&self, other: &SearchScope) -> SearchScope { | ||
29 | let (mut small, mut large) = (&self.entries, &other.entries); | ||
30 | if small.len() > large.len() { | ||
31 | mem::swap(&mut small, &mut large) | ||
32 | } | ||
33 | |||
34 | let res = small | ||
35 | .iter() | ||
36 | .filter_map(|(file_id, r1)| { | ||
37 | let r2 = large.get(file_id)?; | ||
38 | let r = intersect_ranges(*r1, *r2)?; | ||
39 | Some((*file_id, r)) | ||
40 | }) | ||
41 | .collect(); | ||
42 | return SearchScope::new(res); | ||
43 | |||
44 | fn intersect_ranges( | ||
45 | r1: Option<TextRange>, | ||
46 | r2: Option<TextRange>, | ||
47 | ) -> Option<Option<TextRange>> { | ||
48 | match (r1, r2) { | ||
49 | (None, r) | (r, None) => Some(r), | ||
50 | (Some(r1), Some(r2)) => { | ||
51 | let r = r1.intersection(&r2)?; | ||
52 | Some(Some(r)) | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | impl IntoIterator for SearchScope { | ||
60 | type Item = (FileId, Option<TextRange>); | ||
61 | type IntoIter = std::collections::hash_map::IntoIter<FileId, Option<TextRange>>; | ||
62 | fn into_iter(self) -> Self::IntoIter { | ||
63 | self.entries.into_iter() | ||
64 | } | ||
65 | } | ||
66 | |||
67 | impl NameDefinition { | ||
68 | pub(crate) fn search_scope(&self, db: &RootDatabase) -> SearchScope { | ||
69 | let _p = profile("search_scope"); | ||
70 | |||
71 | let module_src = self.container.definition_source(db); | ||
72 | let file_id = module_src.file_id.original_file(db); | ||
73 | |||
74 | if let NameKind::Local(var) = self.kind { | ||
75 | let range = match var.parent(db) { | ||
76 | DefWithBody::Function(f) => f.source(db).value.syntax().text_range(), | ||
77 | DefWithBody::Const(c) => c.source(db).value.syntax().text_range(), | ||
78 | DefWithBody::Static(s) => s.source(db).value.syntax().text_range(), | ||
79 | }; | ||
80 | let mut res = FxHashMap::default(); | ||
81 | res.insert(file_id, Some(range)); | ||
82 | return SearchScope::new(res); | ||
83 | } | ||
84 | |||
85 | let vis = | ||
86 | self.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or("".to_string()); | ||
87 | |||
88 | if vis.as_str() == "pub(super)" { | ||
89 | if let Some(parent_module) = self.container.parent(db) { | ||
90 | let mut res = FxHashMap::default(); | ||
91 | let parent_src = parent_module.definition_source(db); | ||
92 | let file_id = parent_src.file_id.original_file(db); | ||
93 | |||
94 | match parent_src.value { | ||
95 | ModuleSource::Module(m) => { | ||
96 | let range = Some(m.syntax().text_range()); | ||
97 | res.insert(file_id, range); | ||
98 | } | ||
99 | ModuleSource::SourceFile(_) => { | ||
100 | res.insert(file_id, None); | ||
101 | res.extend(parent_module.children(db).map(|m| { | ||
102 | let src = m.definition_source(db); | ||
103 | (src.file_id.original_file(db), None) | ||
104 | })); | ||
105 | } | ||
106 | } | ||
107 | return SearchScope::new(res); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | if vis.as_str() != "" { | ||
112 | let source_root_id = db.file_source_root(file_id); | ||
113 | let source_root = db.source_root(source_root_id); | ||
114 | let mut res = source_root.walk().map(|id| (id, None)).collect::<FxHashMap<_, _>>(); | ||
115 | |||
116 | // FIXME: add "pub(in path)" | ||
117 | |||
118 | if vis.as_str() == "pub(crate)" { | ||
119 | return SearchScope::new(res); | ||
120 | } | ||
121 | if vis.as_str() == "pub" { | ||
122 | let krate = self.container.krate(); | ||
123 | let crate_graph = db.crate_graph(); | ||
124 | for crate_id in crate_graph.iter() { | ||
125 | let mut crate_deps = crate_graph.dependencies(crate_id); | ||
126 | if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) { | ||
127 | let root_file = crate_graph.crate_root(crate_id); | ||
128 | let source_root_id = db.file_source_root(root_file); | ||
129 | let source_root = db.source_root(source_root_id); | ||
130 | res.extend(source_root.walk().map(|id| (id, None))); | ||
131 | } | ||
132 | } | ||
133 | return SearchScope::new(res); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | let mut res = FxHashMap::default(); | ||
138 | let range = match module_src.value { | ||
139 | ModuleSource::Module(m) => Some(m.syntax().text_range()), | ||
140 | ModuleSource::SourceFile(_) => None, | ||
141 | }; | ||
142 | res.insert(file_id, range); | ||
143 | SearchScope::new(res) | ||
144 | } | ||
145 | } | ||