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.rs159
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)
}