@@ -27,6 +27,8 @@ use crate::{
27
27
CompletionConfig ,
28
28
} ;
29
29
30
+ const COMPLETION_MARKER : & str = "intellijRulezz" ;
31
+
30
32
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
31
33
pub ( crate ) enum PatternRefutability {
32
34
Refutable ,
@@ -68,7 +70,7 @@ pub(crate) struct PathCompletionContext {
68
70
#[ derive( Debug ) ]
69
71
pub ( super ) struct PatternContext {
70
72
pub ( super ) refutability : PatternRefutability ,
71
- pub ( super ) is_param : Option < ParamKind > ,
73
+ pub ( super ) param_ctx : Option < ( ast :: ParamList , ast :: Param , ParamKind ) > ,
72
74
pub ( super ) has_type_ascription : bool ,
73
75
}
74
76
@@ -80,10 +82,10 @@ pub(super) enum LifetimeContext {
80
82
LabelDef ,
81
83
}
82
84
83
- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
85
+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
84
86
pub ( crate ) enum ParamKind {
85
- Function ,
86
- Closure ,
87
+ Function ( ast :: Fn ) ,
88
+ Closure ( ast :: ClosureExpr ) ,
87
89
}
88
90
89
91
/// `CompletionContext` is created early during completion to figure out, where
@@ -382,15 +384,15 @@ impl<'a> CompletionContext<'a> {
382
384
// actual completion.
383
385
let file_with_fake_ident = {
384
386
let parse = db. parse ( file_id) ;
385
- let edit = Indel :: insert ( offset, "intellijRulezz" . to_string ( ) ) ;
387
+ let edit = Indel :: insert ( offset, COMPLETION_MARKER . to_string ( ) ) ;
386
388
parse. reparse ( & edit) . tree ( )
387
389
} ;
388
390
let fake_ident_token =
389
391
file_with_fake_ident. syntax ( ) . token_at_offset ( offset) . right_biased ( ) ?;
390
392
391
393
let original_token = original_file. syntax ( ) . token_at_offset ( offset) . left_biased ( ) ?;
392
394
let token = sema. descend_into_macros_single ( original_token. clone ( ) ) ;
393
- let scope = sema. scope_at_offset ( & token, offset) ;
395
+ let scope = sema. scope_at_offset ( & token. parent ( ) ? , offset) ;
394
396
let krate = scope. krate ( ) ;
395
397
let mut locals = vec ! [ ] ;
396
398
scope. process_all_names ( & mut |name, scope| {
@@ -723,7 +725,7 @@ impl<'a> CompletionContext<'a> {
723
725
}
724
726
}
725
727
ast:: NameLike :: Name ( name) => {
726
- self . pattern_ctx = Self :: classify_name ( & self . sema , name) ;
728
+ self . pattern_ctx = Self :: classify_name ( & self . sema , original_file , name) ;
727
729
}
728
730
}
729
731
}
@@ -750,7 +752,11 @@ impl<'a> CompletionContext<'a> {
750
752
} )
751
753
}
752
754
753
- fn classify_name ( _sema : & Semantics < RootDatabase > , name : ast:: Name ) -> Option < PatternContext > {
755
+ fn classify_name (
756
+ _sema : & Semantics < RootDatabase > ,
757
+ original_file : & SyntaxNode ,
758
+ name : ast:: Name ,
759
+ ) -> Option < PatternContext > {
754
760
let bind_pat = name. syntax ( ) . parent ( ) . and_then ( ast:: IdentPat :: cast) ?;
755
761
let is_name_in_field_pat = bind_pat
756
762
. syntax ( )
@@ -763,7 +769,7 @@ impl<'a> CompletionContext<'a> {
763
769
if !bind_pat. is_simple_ident ( ) {
764
770
return None ;
765
771
}
766
- Some ( pattern_context_for ( bind_pat. into ( ) ) )
772
+ Some ( pattern_context_for ( original_file , bind_pat. into ( ) ) )
767
773
}
768
774
769
775
fn classify_name_ref (
@@ -799,15 +805,15 @@ impl<'a> CompletionContext<'a> {
799
805
} ,
800
806
ast:: TupleStructPat ( it) => {
801
807
path_ctx. has_call_parens = true ;
802
- pat_ctx = Some ( pattern_context_for( it. into( ) ) ) ;
808
+ pat_ctx = Some ( pattern_context_for( original_file , it. into( ) ) ) ;
803
809
Some ( PathKind :: Pat )
804
810
} ,
805
811
ast:: RecordPat ( it) => {
806
- pat_ctx = Some ( pattern_context_for( it. into( ) ) ) ;
812
+ pat_ctx = Some ( pattern_context_for( original_file , it. into( ) ) ) ;
807
813
Some ( PathKind :: Pat )
808
814
} ,
809
815
ast:: PathPat ( it) => {
810
- pat_ctx = Some ( pattern_context_for( it. into( ) ) ) ;
816
+ pat_ctx = Some ( pattern_context_for( original_file , it. into( ) ) ) ;
811
817
Some ( PathKind :: Pat )
812
818
} ,
813
819
ast:: MacroCall ( it) => it. excl_token( ) . and( Some ( PathKind :: Mac ) ) ,
@@ -824,12 +830,7 @@ impl<'a> CompletionContext<'a> {
824
830
path_ctx. use_tree_parent = use_tree_parent;
825
831
path_ctx. qualifier = path
826
832
. segment ( )
827
- . and_then ( |it| {
828
- find_node_with_range :: < ast:: PathSegment > (
829
- original_file,
830
- it. syntax ( ) . text_range ( ) ,
831
- )
832
- } )
833
+ . and_then ( |it| find_node_in_file ( original_file, & it) )
833
834
. map ( |it| it. parent_path ( ) ) ;
834
835
return Some ( ( path_ctx, pat_ctx) ) ;
835
836
}
@@ -864,7 +865,7 @@ impl<'a> CompletionContext<'a> {
864
865
}
865
866
}
866
867
867
- fn pattern_context_for ( pat : ast:: Pat ) -> PatternContext {
868
+ fn pattern_context_for ( original_file : & SyntaxNode , pat : ast:: Pat ) -> PatternContext {
868
869
let mut is_param = None ;
869
870
let ( refutability, has_type_ascription) =
870
871
pat
@@ -877,18 +878,21 @@ fn pattern_context_for(pat: ast::Pat) -> PatternContext {
877
878
match node {
878
879
ast:: LetStmt ( let_) => return ( PatternRefutability :: Irrefutable , let_. ty( ) . is_some( ) ) ,
879
880
ast:: Param ( param) => {
880
- let is_closure_param = param
881
- . syntax( )
882
- . ancestors( )
883
- . nth( 2 )
884
- . and_then( ast:: ClosureExpr :: cast)
885
- . is_some( ) ;
886
- is_param = Some ( if is_closure_param {
887
- ParamKind :: Closure
888
- } else {
889
- ParamKind :: Function
890
- } ) ;
891
- return ( PatternRefutability :: Irrefutable , param. ty( ) . is_some( ) )
881
+ let has_type_ascription = param. ty( ) . is_some( ) ;
882
+ is_param = ( || {
883
+ let fake_param_list = param. syntax( ) . parent( ) . and_then( ast:: ParamList :: cast) ?;
884
+ let param_list = find_node_in_file_compensated( original_file, & fake_param_list) ?;
885
+ let param_list_owner = param_list. syntax( ) . parent( ) ?;
886
+ let kind = match_ast! {
887
+ match param_list_owner {
888
+ ast:: ClosureExpr ( closure) => ParamKind :: Closure ( closure) ,
889
+ ast:: Fn ( fn_) => ParamKind :: Function ( fn_) ,
890
+ _ => return None ,
891
+ }
892
+ } ;
893
+ Some ( ( param_list, param, kind) )
894
+ } ) ( ) ;
895
+ return ( PatternRefutability :: Irrefutable , has_type_ascription)
892
896
} ,
893
897
ast:: MatchArm ( _) => PatternRefutability :: Refutable ,
894
898
ast:: Condition ( _) => PatternRefutability :: Refutable ,
@@ -898,11 +902,29 @@ fn pattern_context_for(pat: ast::Pat) -> PatternContext {
898
902
} ;
899
903
( refutability, false )
900
904
} ) ;
901
- PatternContext { refutability, is_param, has_type_ascription }
905
+ PatternContext { refutability, param_ctx : is_param, has_type_ascription }
906
+ }
907
+
908
+ fn find_node_in_file < N : AstNode > ( syntax : & SyntaxNode , node : & N ) -> Option < N > {
909
+ let syntax_range = syntax. text_range ( ) ;
910
+ let range = node. syntax ( ) . text_range ( ) ;
911
+ let intersection = range. intersect ( syntax_range) ?;
912
+ syntax. covering_element ( intersection) . ancestors ( ) . find_map ( N :: cast)
902
913
}
903
914
904
- fn find_node_with_range < N : AstNode > ( syntax : & SyntaxNode , range : TextRange ) -> Option < N > {
905
- syntax. covering_element ( range) . ancestors ( ) . find_map ( N :: cast)
915
+ /// Compensates for the offset introduced by the fake ident
916
+ /// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
917
+ fn find_node_in_file_compensated < N : AstNode > ( syntax : & SyntaxNode , node : & N ) -> Option < N > {
918
+ let syntax_range = syntax. text_range ( ) ;
919
+ let range = node. syntax ( ) . text_range ( ) ;
920
+ let end = range. end ( ) . checked_sub ( TextSize :: try_from ( COMPLETION_MARKER . len ( ) ) . ok ( ) ?) ?;
921
+ if end < range. start ( ) {
922
+ return None ;
923
+ }
924
+ let range = TextRange :: new ( range. start ( ) , end) ;
925
+ // our inserted ident could cause `range` to be go outside of the original syntax, so cap it
926
+ let intersection = range. intersect ( syntax_range) ?;
927
+ syntax. covering_element ( intersection) . ancestors ( ) . find_map ( N :: cast)
906
928
}
907
929
908
930
fn path_or_use_tree_qualifier ( path : & ast:: Path ) -> Option < ( ast:: Path , bool ) > {
0 commit comments