11module ts . NavigateTo {
2- type RawNavigateToItem = { name : string ; fileName : string ; matchKind : MatchKind ; declaration : Declaration } ;
3-
4- enum MatchKind {
5- none = 0 ,
6- exact = 1 ,
7- substring = 2 ,
8- prefix = 3
9- }
10-
11- export function getNavigateToItems ( program : Program , cancellationToken : CancellationTokenObject , searchValue : string , maxResultCount : number ) : NavigateToItem [ ] {
12- // Split search value in terms array
13- var terms = searchValue . split ( " " ) ;
14-
15- // default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version
16- var searchTerms = map ( terms , t => ( { caseSensitive : hasAnyUpperCaseCharacter ( t ) , term : t } ) ) ;
2+ type RawNavigateToItem = { name : string ; fileName : string ; matchKind : PatternMatchKind ; isCaseSensitive : boolean ; declaration : Declaration } ;
173
4+ export function getNavigateToItems ( program : Program , cancellationToken : CancellationTokenObject , searchValue : string , maxResultCount : number ) : NavigateToItem [ ] {
5+ var patternMatcher = createPatternMatcher ( searchValue ) ;
186 var rawItems : RawNavigateToItem [ ] = [ ] ;
197
208 // Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
219 forEach ( program . getSourceFiles ( ) , sourceFile => {
2210 cancellationToken . throwIfCancellationRequested ( ) ;
2311
24- var fileName = sourceFile . fileName ;
2512 var declarations = sourceFile . getNamedDeclarations ( ) ;
2613 for ( var i = 0 , n = declarations . length ; i < n ; i ++ ) {
2714 var declaration = declarations [ i ] ;
28- // TODO(jfreeman): Skip this declaration if it has a computed name
29- var name = ( < Identifier > declaration . name ) . text ;
30- var matchKind = getMatchKind ( searchTerms , name ) ;
31- if ( matchKind !== MatchKind . none ) {
32- rawItems . push ( { name, fileName, matchKind, declaration } ) ;
15+ var name = getDeclarationName ( declaration ) ;
16+ if ( name !== undefined ) {
17+
18+ // First do a quick check to see if the name of the declaration matches the
19+ // last portion of the (possibly) dotted name they're searching for.
20+ var matches = patternMatcher . getMatchesForLastSegmentOfPattern ( name ) ;
21+
22+ if ( ! matches ) {
23+ continue ;
24+ }
25+
26+ // It was a match! If the pattern has dots in it, then also see if hte
27+ // declaration container matches as well.
28+ if ( patternMatcher . patternContainsDots ) {
29+ var containerName = getContainerName ( getContainerNode ( declaration ) ) ;
30+ matches = patternMatcher . getMatches ( name , containerName ) ;
31+
32+ if ( ! matches ) {
33+ continue ;
34+ }
35+ }
36+
37+ var fileName = sourceFile . fileName ;
38+ var matchKind = bestMatchKind ( matches ) ;
39+ rawItems . push ( { name, fileName, matchKind, isCaseSensitive : isCaseSensitive ( matches ) , declaration } ) ;
3340 }
3441 }
3542 } ) ;
@@ -43,6 +50,56 @@ module ts.NavigateTo {
4350
4451 return items ;
4552
53+ function isCaseSensitive ( matches : PatternMatch [ ] ) : boolean {
54+ Debug . assert ( matches . length > 0 ) ;
55+
56+ // This is a case sensitive match, only if all the submatches were case sensitive.
57+ for ( var i = 0 , n = matches . length ; i < n ; i ++ ) {
58+ if ( ! matches [ i ] . isCaseSensitive ) {
59+ return false ;
60+ }
61+ }
62+
63+ return true ;
64+ }
65+
66+ function getDeclarationName ( declaration : Declaration ) : string {
67+ if ( declaration . name . kind === SyntaxKind . Identifier ||
68+ declaration . name . kind === SyntaxKind . StringLiteral ||
69+ declaration . name . kind === SyntaxKind . NumericLiteral ) {
70+
71+ return ( < Identifier > declaration . name ) . text ;
72+ }
73+
74+ return undefined ;
75+ }
76+
77+ function getContainerName ( declaration : Declaration ) : string {
78+ var name = getDeclarationName ( declaration ) ;
79+ if ( name === undefined ) {
80+ return undefined ;
81+ }
82+
83+ var container = getContainerNode ( declaration ) ;
84+ return container && container . name
85+ ? getContainerName ( container ) + "." + name
86+ : name ;
87+ }
88+
89+ function bestMatchKind ( matches : PatternMatch [ ] ) {
90+ Debug . assert ( matches . length > 0 ) ;
91+ var bestMatchKind = PatternMatchKind . camelCase ;
92+
93+ for ( var i = 0 , n = matches . length ; i < n ; i ++ ) {
94+ var kind = matches [ i ] . kind ;
95+ if ( kind < bestMatchKind ) {
96+ bestMatchKind = kind ;
97+ }
98+ }
99+
100+ return bestMatchKind ;
101+ }
102+
46103 // This means "compare in a case insensitive manner."
47104 var baseSensitivity : Intl . CollatorOptions = { sensitivity : "base" } ;
48105 function compareNavigateToItems ( i1 : RawNavigateToItem , i2 : RawNavigateToItem ) {
@@ -62,56 +119,14 @@ module ts.NavigateTo {
62119 name : rawItem . name ,
63120 kind : getNodeKind ( declaration ) ,
64121 kindModifiers : getNodeModifiers ( declaration ) ,
65- matchKind : MatchKind [ rawItem . matchKind ] ,
122+ matchKind : PatternMatchKind [ rawItem . matchKind ] ,
123+ isCaseSensitive : rawItem . isCaseSensitive ,
66124 fileName : rawItem . fileName ,
67125 textSpan : createTextSpanFromBounds ( declaration . getStart ( ) , declaration . getEnd ( ) ) ,
68126 // TODO(jfreeman): What should be the containerName when the container has a computed name?
69127 containerName : container && container . name ? ( < Identifier > container . name ) . text : "" ,
70128 containerKind : container && container . name ? getNodeKind ( container ) : ""
71129 } ;
72130 }
73-
74- function hasAnyUpperCaseCharacter ( s : string ) : boolean {
75- for ( var i = 0 , n = s . length ; i < n ; i ++ ) {
76- var c = s . charCodeAt ( i ) ;
77- if ( ( CharacterCodes . A <= c && c <= CharacterCodes . Z ) ||
78- ( c >= CharacterCodes . maxAsciiCharacter && s . charAt ( i ) . toLocaleLowerCase ( ) !== s . charAt ( i ) ) ) {
79- return true ;
80- }
81- }
82-
83- return false ;
84- }
85-
86- function getMatchKind ( searchTerms : { caseSensitive : boolean ; term : string } [ ] , name : string ) : MatchKind {
87- var matchKind = MatchKind . none ;
88-
89- if ( name ) {
90- for ( var j = 0 , n = searchTerms . length ; j < n ; j ++ ) {
91- var searchTerm = searchTerms [ j ] ;
92- var nameToSearch = searchTerm . caseSensitive ? name : name . toLocaleLowerCase ( ) ;
93- // in case of case-insensitive search searchTerm.term will already be lower-cased
94- var index = nameToSearch . indexOf ( searchTerm . term ) ;
95- if ( index < 0 ) {
96- // Didn't match.
97- return MatchKind . none ;
98- }
99-
100- var termKind = MatchKind . substring ;
101- if ( index === 0 ) {
102- // here we know that match occur at the beginning of the string.
103- // if search term and declName has the same length - we have an exact match, otherwise declName have longer length and this will be prefix match
104- termKind = name . length === searchTerm . term . length ? MatchKind . exact : MatchKind . prefix ;
105- }
106-
107- // Update our match kind if we don't have one, or if this match is better.
108- if ( matchKind === MatchKind . none || termKind < matchKind ) {
109- matchKind = termKind ;
110- }
111- }
112- }
113-
114- return matchKind ;
115- }
116131 }
117132}
0 commit comments