Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/patterns.rs')
| -rw-r--r-- | crates/ide-completion/src/patterns.rs | 159 |
1 files changed, 2 insertions, 157 deletions
diff --git a/crates/ide-completion/src/patterns.rs b/crates/ide-completion/src/patterns.rs index bc46ff7f2d..9efb42c4de 100644 --- a/crates/ide-completion/src/patterns.rs +++ b/crates/ide-completion/src/patterns.rs @@ -4,171 +4,16 @@ //! This means we for example expand a NameRef token to its outermost Path node, as semantically these act in the same location //! and the completions usually query for path specific things on the Path context instead. This simplifies some location handling. -use hir::Semantics; -use ide_db::RootDatabase; use syntax::{ - ast::{self, HasLoopBody, HasName}, + ast::{self, HasLoopBody}, match_ast, AstNode, SyntaxElement, SyntaxKind::*, - SyntaxNode, SyntaxToken, TextRange, TextSize, + SyntaxNode, SyntaxToken, }; #[cfg(test)] use crate::tests::check_pattern_is_applicable; -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum TypeAnnotation { - Let(Option<ast::Pat>), - FnParam(Option<ast::Pat>), - RetType(Option<ast::Expr>), - Const(Option<ast::Expr>), -} - -/// Direct parent "thing" of what we are currently completing. -/// -/// This may contain nodes of the fake file as well as the original, comments on the variants specify -/// from which file the nodes are. -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum ImmediateLocation { - TypeBound, - /// Original file ast node - TypeAnnotation(TypeAnnotation), - // Only set from a type arg - /// Original file ast node - GenericArgList(ast::GenericArgList), -} - -pub(crate) fn determine_location( - sema: &Semantics<RootDatabase>, - original_file: &SyntaxNode, - offset: TextSize, - name_like: &ast::NameLike, -) -> Option<ImmediateLocation> { - let node = match name_like { - ast::NameLike::NameRef(name_ref) => maximize_name_ref(name_ref), - ast::NameLike::Name(name) => name.syntax().clone(), - ast::NameLike::Lifetime(lt) => lt.syntax().clone(), - }; - - match_ast! { - match node { - ast::TypeBoundList(_it) => return Some(ImmediateLocation::TypeBound), - _ => (), - } - }; - - let parent = match node.parent() { - Some(parent) => match ast::MacroCall::cast(parent.clone()) { - // When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call. - // This is usually fine as the node expansion code above already accounts for that with - // the ancestors call, but there is one exception to this which is that when an attribute - // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ. - // FIXME path expr and statement have a similar problem - Some(call) - if call.excl_token().is_none() - && call.token_tree().is_none() - && call.semicolon_token().is_none() => - { - call.syntax().parent()? - } - _ => parent, - }, - // SourceFile - None => return None, - }; - - let res = match_ast! { - match parent { - ast::TypeBound(_) => ImmediateLocation::TypeBound, - ast::TypeBoundList(_) => ImmediateLocation::TypeBound, - ast::GenericArgList(_) => sema - .find_node_at_offset_with_macros(original_file, offset) - .map(ImmediateLocation::GenericArgList)?, - ast::Const(it) => { - if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) { - return None; - } - let name = find_in_original_file(it.name(), original_file)?; - let original = ast::Const::cast(name.syntax().parent()?)?; - ImmediateLocation::TypeAnnotation(TypeAnnotation::Const(original.body())) - }, - ast::RetType(it) => { - if it.thin_arrow_token().is_none() { - return None; - } - if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) { - return None; - } - let parent = match ast::Fn::cast(parent.parent()?) { - Some(x) => x.param_list(), - None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(), - }; - let parent = find_in_original_file(parent, original_file)?.syntax().parent()?; - ImmediateLocation::TypeAnnotation(TypeAnnotation::RetType(match_ast! { - match parent { - ast::ClosureExpr(it) => { - it.body() - }, - ast::Fn(it) => { - it.body().map(ast::Expr::BlockExpr) - }, - _ => return None, - } - })) - }, - ast::Param(it) => { - if it.colon_token().is_none() { - return None; - } - if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) { - return None; - } - ImmediateLocation::TypeAnnotation(TypeAnnotation::FnParam(find_in_original_file(it.pat(), original_file))) - }, - ast::LetStmt(it) => { - if it.colon_token().is_none() { - return None; - } - if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) { - return None; - } - ImmediateLocation::TypeAnnotation(TypeAnnotation::Let(find_in_original_file(it.pat(), original_file))) - }, - _ => return None, - } - }; - fn find_in_original_file<N: AstNode>(x: Option<N>, original_file: &SyntaxNode) -> Option<N> { - x.map(|e| e.syntax().text_range()).and_then(|r| find_node_with_range(original_file, r)) - } - Some(res) -} - -/// Maximize a nameref to its enclosing path if its the last segment of said path. -/// That is, when completing a [`NameRef`] we actually handle it as the path it is part of when determining -/// its location. -fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode { - if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) { - let p = segment.parent_path(); - if p.parent_path().is_none() { - // Get rid of PathExpr, PathType, etc... - let path = p - .syntax() - .ancestors() - .take_while(|it| it.text_range() == p.syntax().text_range()) - .last(); - if let Some(it) = path { - return it; - } - } - } - name_ref.syntax().clone() -} - -fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> { - let range = syntax.text_range().intersect(range)?; - syntax.covering_element(range).ancestors().find_map(N::cast) -} - pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { element.into_token().and_then(previous_non_trivia_token) } |