aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/source_analyzer.rs99
1 files changed, 52 insertions, 47 deletions
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 58ae6ce41..23af400b8 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -23,7 +23,7 @@ use hir_ty::{
23}; 23};
24use ra_syntax::{ 24use ra_syntax::{
25 ast::{self, AstNode}, 25 ast::{self, AstNode},
26 SyntaxNode, SyntaxNodePtr, TextUnit, 26 SyntaxNode, TextRange, TextUnit,
27}; 27};
28 28
29use crate::{ 29use crate::{
@@ -56,7 +56,7 @@ impl SourceAnalyzer {
56 let scopes = db.expr_scopes(def); 56 let scopes = db.expr_scopes(def);
57 let scope = match offset { 57 let scope = match offset {
58 None => scope_for(&scopes, &source_map, node), 58 None => scope_for(&scopes, &source_map, node),
59 Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)), 59 Some(offset) => scope_for_offset(db, &scopes, &source_map, node.with_value(offset)),
60 }; 60 };
61 let resolver = resolver_for_scope(db.upcast(), def, scope); 61 let resolver = resolver_for_scope(db.upcast(), def, scope);
62 SourceAnalyzer { 62 SourceAnalyzer {
@@ -304,6 +304,7 @@ fn scope_for(
304} 304}
305 305
306fn scope_for_offset( 306fn scope_for_offset(
307 db: &dyn HirDatabase,
307 scopes: &ExprScopes, 308 scopes: &ExprScopes,
308 source_map: &BodySourceMap, 309 source_map: &BodySourceMap,
309 offset: InFile<TextUnit>, 310 offset: InFile<TextUnit>,
@@ -317,21 +318,63 @@ fn scope_for_offset(
317 if source.file_id != offset.file_id { 318 if source.file_id != offset.file_id {
318 return None; 319 return None;
319 } 320 }
320 let syntax_node_ptr = source.value.syntax_node_ptr(); 321 let root = source.file_syntax(db.upcast());
321 Some((syntax_node_ptr, scope)) 322 let node = source.value.to_node(&root);
323 Some((node.syntax().text_range(), scope))
322 }) 324 })
323 // find containing scope 325 // find containing scope
324 .min_by_key(|(ptr, _scope)| { 326 .min_by_key(|(expr_range, _scope)| {
325 ( 327 (
326 !(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()), 328 !(expr_range.start() <= offset.value && offset.value <= expr_range.end()),
327 ptr.range().len(), 329 expr_range.len(),
328 ) 330 )
329 }) 331 })
330 .map(|(ptr, scope)| { 332 .map(|(expr_range, scope)| {
331 adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope) 333 adjust(db, scopes, source_map, expr_range, offset.file_id, offset.value)
334 .unwrap_or(*scope)
332 }) 335 })
333} 336}
334 337
338// XXX: during completion, cursor might be outside of any particular
339// expression. Try to figure out the correct scope...
340fn adjust(
341 db: &dyn HirDatabase,
342 scopes: &ExprScopes,
343 source_map: &BodySourceMap,
344 expr_range: TextRange,
345 file_id: HirFileId,
346 offset: TextUnit,
347) -> Option<ScopeId> {
348 let child_scopes = scopes
349 .scope_by_expr()
350 .iter()
351 .filter_map(|(id, scope)| {
352 let source = source_map.expr_syntax(*id).ok()?;
353 // FIXME: correctly handle macro expansion
354 if source.file_id != file_id {
355 return None;
356 }
357 let root = source.file_syntax(db.upcast());
358 let node = source.value.to_node(&root);
359 Some((node.syntax().text_range(), scope))
360 })
361 .filter(|(range, _)| {
362 range.start() <= offset && range.is_subrange(&expr_range) && *range != expr_range
363 });
364
365 child_scopes
366 .max_by(|(r1, _), (r2, _)| {
367 if r2.is_subrange(&r1) {
368 std::cmp::Ordering::Greater
369 } else if r1.is_subrange(&r2) {
370 std::cmp::Ordering::Less
371 } else {
372 r1.start().cmp(&r2.start())
373 }
374 })
375 .map(|(_ptr, scope)| *scope)
376}
377
335pub(crate) fn resolve_hir_path( 378pub(crate) fn resolve_hir_path(
336 db: &dyn HirDatabase, 379 db: &dyn HirDatabase,
337 resolver: &Resolver, 380 resolver: &Resolver,
@@ -376,41 +419,3 @@ pub(crate) fn resolve_hir_path(
376 .map(|def| PathResolution::Macro(def.into())) 419 .map(|def| PathResolution::Macro(def.into()))
377 }) 420 })
378} 421}
379
380// XXX: during completion, cursor might be outside of any particular
381// expression. Try to figure out the correct scope...
382fn adjust(
383 scopes: &ExprScopes,
384 source_map: &BodySourceMap,
385 ptr: SyntaxNodePtr,
386 file_id: HirFileId,
387 offset: TextUnit,
388) -> Option<ScopeId> {
389 let r = ptr.range();
390 let child_scopes = scopes
391 .scope_by_expr()
392 .iter()
393 .filter_map(|(id, scope)| {
394 let source = source_map.expr_syntax(*id).ok()?;
395 // FIXME: correctly handle macro expansion
396 if source.file_id != file_id {
397 return None;
398 }
399 let syntax_node_ptr = source.value.syntax_node_ptr();
400 Some((syntax_node_ptr, scope))
401 })
402 .map(|(ptr, scope)| (ptr.range(), scope))
403 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
404
405 child_scopes
406 .max_by(|(r1, _), (r2, _)| {
407 if r2.is_subrange(&r1) {
408 std::cmp::Ordering::Greater
409 } else if r1.is_subrange(&r2) {
410 std::cmp::Ordering::Less
411 } else {
412 r1.start().cmp(&r2.start())
413 }
414 })
415 .map(|(_ptr, scope)| *scope)
416}