aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs7
-rw-r--r--crates/ra_analysis/src/descriptors/mod.rs5
-rw-r--r--crates/ra_analysis/src/descriptors/module/nameres.rs182
-rw-r--r--crates/ra_analysis/src/descriptors/path.rs19
-rw-r--r--crates/ra_analysis/src/loc2id.rs5
-rw-r--r--crates/ra_analysis/src/syntax_ptr.rs5
6 files changed, 139 insertions, 84 deletions
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
index d301a3c02..1faa70a18 100644
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ b/crates/ra_analysis/src/completion/reference_completion.rs
@@ -46,9 +46,12 @@ pub(super) fn completions(
46 .iter() 46 .iter()
47 .filter(|(_name, res)| { 47 .filter(|(_name, res)| {
48 // Don't expose this item 48 // Don't expose this item
49 match res.import_name { 49 match res.import {
50 None => true, 50 None => true,
51 Some(ptr) => !ptr.range().is_subrange(&name_ref.syntax().range()), 51 Some(import) => {
52 let range = import.range(db, module.source().file_id());
53 !range.is_subrange(&name_ref.syntax().range())
54 }
52 } 55 }
53 }) 56 })
54 .map(|(name, _res)| CompletionItem { 57 .map(|(name, _res)| CompletionItem {
diff --git a/crates/ra_analysis/src/descriptors/mod.rs b/crates/ra_analysis/src/descriptors/mod.rs
index f6b9102e7..a5e956024 100644
--- a/crates/ra_analysis/src/descriptors/mod.rs
+++ b/crates/ra_analysis/src/descriptors/mod.rs
@@ -13,7 +13,7 @@ use crate::{
13 FileId, 13 FileId,
14 db::SyntaxDatabase, 14 db::SyntaxDatabase,
15 descriptors::function::{resolve_local_name, FnId, FnScopes}, 15 descriptors::function::{resolve_local_name, FnId, FnScopes},
16 descriptors::module::{ModuleId, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems, FileItemId}}, 16 descriptors::module::{ModuleId, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems, FileItems}},
17 input::SourceRootId, 17 input::SourceRootId,
18 loc2id::IdDatabase, 18 loc2id::IdDatabase,
19 syntax_ptr::LocalSyntaxPtr, 19 syntax_ptr::LocalSyntaxPtr,
@@ -21,6 +21,7 @@ use crate::{
21}; 21};
22 22
23pub(crate) use self::path::{Path, PathKind}; 23pub(crate) use self::path::{Path, PathKind};
24pub(crate) use self::module::nameres::FileItemId;
24 25
25salsa::query_group! { 26salsa::query_group! {
26 pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase { 27 pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase {
@@ -29,7 +30,7 @@ salsa::query_group! {
29 use fn function::imp::fn_scopes; 30 use fn function::imp::fn_scopes;
30 } 31 }
31 32
32 fn _file_items(file_id: FileId) -> Arc<Vec<SyntaxNode>> { 33 fn _file_items(file_id: FileId) -> Arc<FileItems> {
33 type FileItemsQuery; 34 type FileItemsQuery;
34 storage volatile; 35 storage volatile;
35 use fn module::nameres::file_items; 36 use fn module::nameres::file_items;
diff --git a/crates/ra_analysis/src/descriptors/module/nameres.rs b/crates/ra_analysis/src/descriptors/module/nameres.rs
index 6e327e374..d347a69b0 100644
--- a/crates/ra_analysis/src/descriptors/module/nameres.rs
+++ b/crates/ra_analysis/src/descriptors/module/nameres.rs
@@ -17,12 +17,13 @@
17use std::{ 17use std::{
18 sync::Arc, 18 sync::Arc,
19 time::Instant, 19 time::Instant,
20 ops::Index,
20}; 21};
21 22
22use rustc_hash::FxHashMap; 23use rustc_hash::FxHashMap;
23 24
24use ra_syntax::{ 25use ra_syntax::{
25 SyntaxNode, 26 SyntaxNode, SyntaxNodeRef, TextRange,
26 SmolStr, SyntaxKind::{self, *}, 27 SmolStr, SyntaxKind::{self, *},
27 ast::{self, ModuleItemOwner, AstNode} 28 ast::{self, ModuleItemOwner, AstNode}
28}; 29};
@@ -35,28 +36,62 @@ use crate::{
35 DescriptorDatabase, 36 DescriptorDatabase,
36 module::{ModuleId, ModuleTree, ModuleSourceNode}, 37 module::{ModuleId, ModuleTree, ModuleSourceNode},
37 }, 38 },
38 syntax_ptr::{LocalSyntaxPtr},
39 input::SourceRootId, 39 input::SourceRootId,
40 arena::{Arena, Id}
40}; 41};
41 42
43/// Identifier of item within a specific file. This is stable over reparses, so
44/// it's OK to use it as a salsa key/value.
45pub(crate) type FileItemId = Id<SyntaxNode>;
42 46
43#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] 47/// Maps item's `SyntaxNode`s to `FileItemId` and back.
44pub(crate) struct FileItemId(u32); 48#[derive(Debug, PartialEq, Eq, Default)]
49pub(crate) struct FileItems {
50 arena: Arena<SyntaxNode>,
51}
52
53impl FileItems {
54 fn alloc(&mut self, item: SyntaxNode) -> FileItemId {
55 self.arena.alloc(item)
56 }
57 fn id_of(&self, item: SyntaxNodeRef) -> FileItemId {
58 let (id, _item) = self
59 .arena
60 .iter()
61 .find(|(_id, i)| i.borrowed() == item)
62 .unwrap();
63 id
64 }
65}
45 66
46pub(crate) fn file_items(db: &impl DescriptorDatabase, file_id: FileId) -> Arc<Vec<SyntaxNode>> { 67impl Index<FileItemId> for FileItems {
68 type Output = SyntaxNode;
69 fn index(&self, idx: FileItemId) -> &SyntaxNode {
70 &self.arena[idx]
71 }
72}
73
74pub(crate) fn file_items(db: &impl DescriptorDatabase, file_id: FileId) -> Arc<FileItems> {
47 let source_file = db.file_syntax(file_id); 75 let source_file = db.file_syntax(file_id);
48 let source_file = source_file.borrowed(); 76 let source_file = source_file.borrowed();
49 let res = source_file.syntax().descendants() 77 let mut res = FileItems::default();
78 source_file
79 .syntax()
80 .descendants()
50 .filter_map(ast::ModuleItem::cast) 81 .filter_map(ast::ModuleItem::cast)
51 .map(|it| it.syntax().owned()) 82 .map(|it| it.syntax().owned())
52 .collect::<Vec<_>>(); 83 .for_each(|it| {
84 res.alloc(it);
85 });
53 Arc::new(res) 86 Arc::new(res)
54} 87}
55 88
56pub(crate) fn file_item(db: &impl DescriptorDatabase, file_id: FileId, file_item_id: FileItemId) -> SyntaxNode { 89pub(crate) fn file_item(
57 let items = db._file_items(file_id); 90 db: &impl DescriptorDatabase,
58 let idx = file_item_id.0 as usize; 91 file_id: FileId,
59 items[idx].clone() 92 file_item_id: FileItemId,
93) -> SyntaxNode {
94 db._file_items(file_id)[file_item_id].clone()
60} 95}
61 96
62/// Item map is the result of the name resolution. Item map contains, for each 97/// Item map is the result of the name resolution. Item map contains, for each
@@ -83,17 +118,44 @@ pub(crate) struct InputModuleItems {
83 imports: Vec<Import>, 118 imports: Vec<Import>,
84} 119}
85 120
121#[derive(Debug, PartialEq, Eq)]
122struct ModuleItem {
123 id: FileItemId,
124 name: SmolStr,
125 kind: SyntaxKind,
126 vis: Vis,
127}
128
129#[derive(Debug, PartialEq, Eq)]
130enum Vis {
131 // Priv,
132 Other,
133}
134
86#[derive(Debug, Clone, PartialEq, Eq)] 135#[derive(Debug, Clone, PartialEq, Eq)]
87struct Import { 136struct Import {
88 path: Path, 137 path: Path,
89 kind: ImportKind, 138 kind: ImportKind,
90} 139}
91 140
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub(crate) struct NamedImport {
143 file_item_id: FileItemId,
144 relative_range: TextRange,
145}
146
147impl NamedImport {
148 pub(crate) fn range(&self, db: &impl DescriptorDatabase, file_id: FileId) -> TextRange {
149 let syntax = db._file_item(file_id, self.file_item_id);
150 let offset = syntax.borrowed().range().start();
151 self.relative_range + offset
152 }
153}
154
92#[derive(Debug, Clone, PartialEq, Eq)] 155#[derive(Debug, Clone, PartialEq, Eq)]
93enum ImportKind { 156enum ImportKind {
94 Glob, 157 Glob,
95 // TODO: make offset independent 158 Named(NamedImport),
96 Named(LocalSyntaxPtr),
97} 159}
98 160
99pub(crate) fn input_module_items( 161pub(crate) fn input_module_items(
@@ -103,10 +165,11 @@ pub(crate) fn input_module_items(
103) -> Cancelable<Arc<InputModuleItems>> { 165) -> Cancelable<Arc<InputModuleItems>> {
104 let module_tree = db._module_tree(source_root)?; 166 let module_tree = db._module_tree(source_root)?;
105 let source = module_id.source(&module_tree); 167 let source = module_id.source(&module_tree);
168 let file_items = db._file_items(source.file_id());
106 let res = match source.resolve(db) { 169 let res = match source.resolve(db) {
107 ModuleSourceNode::SourceFile(it) => { 170 ModuleSourceNode::SourceFile(it) => {
108 let items = it.borrowed().items(); 171 let items = it.borrowed().items();
109 InputModuleItems::new(items) 172 InputModuleItems::new(&file_items, items)
110 } 173 }
111 ModuleSourceNode::Module(it) => { 174 ModuleSourceNode::Module(it) => {
112 let items = it 175 let items = it
@@ -114,7 +177,7 @@ pub(crate) fn input_module_items(
114 .item_list() 177 .item_list()
115 .into_iter() 178 .into_iter()
116 .flat_map(|it| it.items()); 179 .flat_map(|it| it.items());
117 InputModuleItems::new(items) 180 InputModuleItems::new(&file_items, items)
118 } 181 }
119 }; 182 };
120 Ok(Arc::new(res)) 183 Ok(Arc::new(res))
@@ -133,7 +196,6 @@ pub(crate) fn item_map(
133 Ok((id, items)) 196 Ok((id, items))
134 }) 197 })
135 .collect::<Cancelable<FxHashMap<_, _>>>()?; 198 .collect::<Cancelable<FxHashMap<_, _>>>()?;
136
137 let mut resolver = Resolver { 199 let mut resolver = Resolver {
138 db: db, 200 db: db,
139 input: &input, 201 input: &input,
@@ -155,8 +217,7 @@ pub(crate) struct Resolution {
155 /// None for unresolved 217 /// None for unresolved
156 pub(crate) def_id: Option<DefId>, 218 pub(crate) def_id: Option<DefId>,
157 /// ident by whitch this is imported into local scope. 219 /// ident by whitch this is imported into local scope.
158 /// TODO: make this offset-independent. 220 pub(crate) import: Option<NamedImport>,
159 pub(crate) import_name: Option<LocalSyntaxPtr>,
160} 221}
161 222
162// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 223// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -171,55 +232,49 @@ pub(crate) struct Resolution {
171// values: Option<T>, 232// values: Option<T>,
172// } 233// }
173 234
174#[derive(Debug, PartialEq, Eq)]
175struct ModuleItem {
176 ptr: LocalSyntaxPtr,
177 name: SmolStr,
178 kind: SyntaxKind,
179 vis: Vis,
180}
181
182#[derive(Debug, PartialEq, Eq)]
183enum Vis {
184 // Priv,
185 Other,
186}
187
188impl InputModuleItems { 235impl InputModuleItems {
189 fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> InputModuleItems { 236 fn new<'a>(
237 file_items: &FileItems,
238 items: impl Iterator<Item = ast::ModuleItem<'a>>,
239 ) -> InputModuleItems {
190 let mut res = InputModuleItems::default(); 240 let mut res = InputModuleItems::default();
191 for item in items { 241 for item in items {
192 res.add_item(item); 242 res.add_item(file_items, item);
193 } 243 }
194 res 244 res
195 } 245 }
196 246
197 fn add_item(&mut self, item: ast::ModuleItem) -> Option<()> { 247 fn add_item(&mut self, file_items: &FileItems, item: ast::ModuleItem) -> Option<()> {
198 match item { 248 match item {
199 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(it)?), 249 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
200 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(it)?), 250 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
201 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(it)?), 251 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
202 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(it)?), 252 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
203 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(it)?), 253 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
204 ast::ModuleItem::ImplItem(_) => { 254 ast::ModuleItem::ImplItem(_) => {
205 // impls don't define items 255 // impls don't define items
206 } 256 }
207 ast::ModuleItem::UseItem(it) => self.add_use_item(it), 257 ast::ModuleItem::UseItem(it) => self.add_use_item(file_items, it),
208 ast::ModuleItem::ExternCrateItem(_) => { 258 ast::ModuleItem::ExternCrateItem(_) => {
209 // TODO 259 // TODO
210 } 260 }
211 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(it)?), 261 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
212 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(it)?), 262 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
213 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(it)?), 263 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(file_items, it)?),
214 } 264 }
215 Some(()) 265 Some(())
216 } 266 }
217 267
218 fn add_use_item(&mut self, item: ast::UseItem) { 268 fn add_use_item(&mut self, file_items: &FileItems, item: ast::UseItem) {
219 Path::expand_use_item(item, |path, ptr| { 269 let file_item_id = file_items.id_of(item.syntax());
220 let kind = match ptr { 270 let start_offset = item.syntax().range().start();
271 Path::expand_use_item(item, |path, range| {
272 let kind = match range {
221 None => ImportKind::Glob, 273 None => ImportKind::Glob,
222 Some(ptr) => ImportKind::Named(ptr), 274 Some(range) => ImportKind::Named(NamedImport {
275 file_item_id,
276 relative_range: range - start_offset,
277 }),
223 }; 278 };
224 self.imports.push(Import { kind, path }) 279 self.imports.push(Import { kind, path })
225 }) 280 })
@@ -227,13 +282,13 @@ impl InputModuleItems {
227} 282}
228 283
229impl ModuleItem { 284impl ModuleItem {
230 fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { 285 fn new<'a>(file_items: &FileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
231 let name = item.name()?.text(); 286 let name = item.name()?.text();
232 let ptr = LocalSyntaxPtr::new(item.syntax());
233 let kind = item.syntax().kind(); 287 let kind = item.syntax().kind();
234 let vis = Vis::Other; 288 let vis = Vis::Other;
289 let id = file_items.id_of(item.syntax());
235 let res = ModuleItem { 290 let res = ModuleItem {
236 ptr, 291 id,
237 name, 292 name,
238 kind, 293 kind,
239 vis, 294 vis,
@@ -273,12 +328,12 @@ where
273 328
274 for import in input.imports.iter() { 329 for import in input.imports.iter() {
275 if let Some(name) = import.path.segments.iter().last() { 330 if let Some(name) = import.path.segments.iter().last() {
276 if let ImportKind::Named(ptr) = import.kind { 331 if let ImportKind::Named(import) = import.kind {
277 module_items.items.insert( 332 module_items.items.insert(
278 name.clone(), 333 name.clone(),
279 Resolution { 334 Resolution {
280 def_id: None, 335 def_id: None,
281 import_name: Some(ptr), 336 import: Some(import),
282 }, 337 },
283 ); 338 );
284 } 339 }
@@ -290,12 +345,14 @@ where
290 // handle submodules separatelly 345 // handle submodules separatelly
291 continue; 346 continue;
292 } 347 }
293 let ptr = item.ptr.into_global(file_id); 348 let def_loc = DefLoc::Item {
294 let def_loc = DefLoc::Item { ptr }; 349 file_id,
350 id: item.id,
351 };
295 let def_id = self.db.id_maps().def_id(def_loc); 352 let def_id = self.db.id_maps().def_id(def_loc);
296 let resolution = Resolution { 353 let resolution = Resolution {
297 def_id: Some(def_id), 354 def_id: Some(def_id),
298 import_name: None, 355 import: None,
299 }; 356 };
300 module_items.items.insert(item.name.clone(), resolution); 357 module_items.items.insert(item.name.clone(), resolution);
301 } 358 }
@@ -308,7 +365,7 @@ where
308 let def_id = self.db.id_maps().def_id(def_loc); 365 let def_id = self.db.id_maps().def_id(def_loc);
309 let resolution = Resolution { 366 let resolution = Resolution {
310 def_id: Some(def_id), 367 def_id: Some(def_id),
311 import_name: None, 368 import: None,
312 }; 369 };
313 module_items.items.insert(name, resolution); 370 module_items.items.insert(name, resolution);
314 } 371 }
@@ -362,7 +419,7 @@ where
362 self.update(module_id, |items| { 419 self.update(module_id, |items| {
363 let res = Resolution { 420 let res = Resolution {
364 def_id: Some(def_id), 421 def_id: Some(def_id),
365 import_name: Some(ptr), 422 import: Some(ptr),
366 }; 423 };
367 items.items.insert(name.clone(), res); 424 items.items.insert(name.clone(), res);
368 }) 425 })
@@ -473,10 +530,11 @@ mod tests {
473 let events = db.log_executed(|| { 530 let events = db.log_executed(|| {
474 db._item_map(source_root).unwrap(); 531 db._item_map(source_root).unwrap();
475 }); 532 });
476 // assert!( 533 assert!(
477 // !format!("{:?}", events).contains("_item_map"), 534 !format!("{:?}", events).contains("_item_map"),
478 // "{:#?}", events 535 "{:#?}",
479 // ) 536 events
537 )
480 } 538 }
481 } 539 }
482} 540}
diff --git a/crates/ra_analysis/src/descriptors/path.rs b/crates/ra_analysis/src/descriptors/path.rs
index 99fca18b1..8279daf4b 100644
--- a/crates/ra_analysis/src/descriptors/path.rs
+++ b/crates/ra_analysis/src/descriptors/path.rs
@@ -1,6 +1,4 @@
1use ra_syntax::{SmolStr, ast, AstNode}; 1use ra_syntax::{SmolStr, ast, AstNode, TextRange};
2
3use crate::syntax_ptr::LocalSyntaxPtr;
4 2
5#[derive(Debug, Clone, PartialEq, Eq)] 3#[derive(Debug, Clone, PartialEq, Eq)]
6pub(crate) struct Path { 4pub(crate) struct Path {
@@ -18,10 +16,7 @@ pub(crate) enum PathKind {
18 16
19impl Path { 17impl Path {
20 /// Calls `cb` with all paths, represented by this use item. 18 /// Calls `cb` with all paths, represented by this use item.
21 pub(crate) fn expand_use_item( 19 pub(crate) fn expand_use_item(item: ast::UseItem, mut cb: impl FnMut(Path, Option<TextRange>)) {
22 item: ast::UseItem,
23 mut cb: impl FnMut(Path, Option<LocalSyntaxPtr>),
24 ) {
25 if let Some(tree) = item.use_tree() { 20 if let Some(tree) = item.use_tree() {
26 expand_use_tree(None, tree, &mut cb); 21 expand_use_tree(None, tree, &mut cb);
27 } 22 }
@@ -77,7 +72,7 @@ impl Path {
77fn expand_use_tree( 72fn expand_use_tree(
78 prefix: Option<Path>, 73 prefix: Option<Path>,
79 tree: ast::UseTree, 74 tree: ast::UseTree,
80 cb: &mut impl FnMut(Path, Option<LocalSyntaxPtr>), 75 cb: &mut impl FnMut(Path, Option<TextRange>),
81) { 76) {
82 if let Some(use_tree_list) = tree.use_tree_list() { 77 if let Some(use_tree_list) = tree.use_tree_list() {
83 let prefix = match tree.path() { 78 let prefix = match tree.path() {
@@ -93,13 +88,13 @@ fn expand_use_tree(
93 } else { 88 } else {
94 if let Some(ast_path) = tree.path() { 89 if let Some(ast_path) = tree.path() {
95 if let Some(path) = convert_path(prefix, ast_path) { 90 if let Some(path) = convert_path(prefix, ast_path) {
96 let ptr = if tree.has_star() { 91 let range = if tree.has_star() {
97 None 92 None
98 } else { 93 } else {
99 let ptr = LocalSyntaxPtr::new(ast_path.segment().unwrap().syntax()); 94 let range = ast_path.segment().unwrap().syntax().range();
100 Some(ptr) 95 Some(range)
101 }; 96 };
102 cb(path, ptr) 97 cb(path, range)
103 } 98 }
104 } 99 }
105 } 100 }
diff --git a/crates/ra_analysis/src/loc2id.rs b/crates/ra_analysis/src/loc2id.rs
index e4b55f9b0..c7c799a91 100644
--- a/crates/ra_analysis/src/loc2id.rs
+++ b/crates/ra_analysis/src/loc2id.rs
@@ -8,6 +8,8 @@ use std::{
8use rustc_hash::FxHashMap; 8use rustc_hash::FxHashMap;
9 9
10use crate::{ 10use crate::{
11 FileId,
12 descriptors::FileItemId,
11 descriptors::module::ModuleId, 13 descriptors::module::ModuleId,
12 syntax_ptr::SyntaxPtr, 14 syntax_ptr::SyntaxPtr,
13 input::SourceRootId, 15 input::SourceRootId,
@@ -102,7 +104,8 @@ pub(crate) enum DefLoc {
102 source_root: SourceRootId, 104 source_root: SourceRootId,
103 }, 105 },
104 Item { 106 Item {
105 ptr: SyntaxPtr, 107 file_id: FileId,
108 id: FileItemId,
106 }, 109 },
107} 110}
108 111
diff --git a/crates/ra_analysis/src/syntax_ptr.rs b/crates/ra_analysis/src/syntax_ptr.rs
index 194b94584..e45934ce0 100644
--- a/crates/ra_analysis/src/syntax_ptr.rs
+++ b/crates/ra_analysis/src/syntax_ptr.rs
@@ -62,11 +62,6 @@ impl LocalSyntaxPtr {
62 local: self, 62 local: self,
63 } 63 }
64 } 64 }
65
66 // Seems unfortunate to expose
67 pub(crate) fn range(self) -> TextRange {
68 self.range
69 }
70} 65}
71 66
72#[test] 67#[test]