diff options
-rw-r--r-- | crates/ra_hir/src/source_analyzer.rs | 99 |
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 | }; |
24 | use ra_syntax::{ | 24 | use ra_syntax::{ |
25 | ast::{self, AstNode}, | 25 | ast::{self, AstNode}, |
26 | SyntaxNode, SyntaxNodePtr, TextUnit, | 26 | SyntaxNode, TextRange, TextUnit, |
27 | }; | 27 | }; |
28 | 28 | ||
29 | use crate::{ | 29 | use 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 | ||
306 | fn scope_for_offset( | 306 | fn 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... | ||
340 | fn 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 | |||
335 | pub(crate) fn resolve_hir_path( | 378 | pub(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... | ||
382 | fn 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 | } | ||