@@ -2066,6 +2066,7 @@ module ts {
2066
2066
}
2067
2067
}
2068
2068
2069
+ /** Get the token whose text contains the position, or the containing node. */
2069
2070
function getNodeAtPosition ( sourceFile : SourceFile , position : number ) {
2070
2071
var current : Node = sourceFile ;
2071
2072
outer: while ( true ) {
@@ -2076,9 +2077,24 @@ module ts {
2076
2077
current = child ;
2077
2078
continue outer;
2078
2079
}
2079
- if ( child . end > position ) {
2080
- break ;
2081
- }
2080
+ }
2081
+ return current ;
2082
+ }
2083
+ }
2084
+
2085
+ /** Get a token that contains the position. This is guaranteed to return a token, the position can be in the
2086
+ * leading trivia or within the token text.
2087
+ */
2088
+ function getTokenAtPosition ( sourceFile : SourceFile , position : number ) {
2089
+ var current : Node = sourceFile ;
2090
+ outer: while ( true ) {
2091
+ // find the child that has this
2092
+ for ( var i = 0 , n = current . getChildCount ( ) ; i < n ; i ++ ) {
2093
+ var child = current . getChildAt ( i ) ;
2094
+ if ( child . getFullStart ( ) <= position && position < child . getEnd ( ) ) {
2095
+ current = child ;
2096
+ continue outer;
2097
+ }
2082
2098
}
2083
2099
return current ;
2084
2100
}
@@ -3793,83 +3809,21 @@ module ts {
3793
3809
return [ ] ;
3794
3810
}
3795
3811
3796
- function escapeRegExp ( str : string ) : string {
3797
- return str . replace ( / [ \- \[ \] \/ \{ \} \( \) \* \+ \? \. \\ \^ \$ \| ] / g, "\\$&" ) ;
3798
- }
3799
-
3800
- function getTodoCommentsRegExp ( descriptors : TodoCommentDescriptor [ ] ) : RegExp {
3801
- // NOTE: ?: means 'non-capture group'. It allows us to have groups without having to
3802
- // filter them out later in the final result array.
3803
-
3804
- // TODO comments can appear in one of the following forms:
3805
- //
3806
- // 1) // TODO or /////////// TODO
3807
- //
3808
- // 2) /* TODO or /********** TODO
3809
- //
3810
- // 3) /*
3811
- // * TODO
3812
- // */
3813
- //
3814
- // The following three regexps are used to match the start of the text up to the TODO
3815
- // comment portion.
3816
- var singleLineCommentStart = / (?: \/ \/ + \s * ) / . source ;
3817
- var multiLineCommentStart = / (?: \/ \* + \s * ) / . source ;
3818
- var anyNumberOfSpacesAndAsterixesAtStartOfLine = / (?: ^ (?: \s | \* ) * ) / . source ;
3819
-
3820
- // Match any of the above three TODO comment start regexps.
3821
- // Note that the outermost group *is* a capture group. We want to capture the preamble
3822
- // so that we can determine the starting position of the TODO comment match.
3823
- var preamble = "(" + anyNumberOfSpacesAndAsterixesAtStartOfLine + "|" + singleLineCommentStart + "|" + multiLineCommentStart + ")" ;
3824
-
3825
- // Takes the descriptors and forms a regexp that matches them as if they were literals.
3826
- // For example, if the descriptors are "TODO(jason)" and "HACK", then this will be:
3827
- //
3828
- // (?:(TODO\(jason\))|(HACK))
3829
- //
3830
- // Note that the outermost group is *not* a capture group, but the innermost groups
3831
- // *are* capture groups. By capturing the inner literals we can determine after
3832
- // matching which descriptor we are dealing with.
3833
- var literals = "(?:" + descriptors . map ( d => "(" + escapeRegExp ( d . text ) + ")" ) . join ( "|" ) + ")" ;
3834
-
3835
- // After matching a descriptor literal, the following regexp matches the rest of the
3836
- // text up to the end of the line (or */).
3837
- var endOfLineOrEndOfComment = / (?: $ | \* \/ ) / . source
3838
- var messageRemainder = / (?: .* ?) / . source
3839
-
3840
- // This is the portion of the match we'll return as part of the TODO comment result. We
3841
- // match the literal portion up to the end of the line or end of comment.
3842
- var messagePortion = "(" + literals + messageRemainder + ")" ;
3843
- var regExpString = preamble + messagePortion + endOfLineOrEndOfComment ;
3844
-
3845
- // The final regexp will look like this:
3846
- // /((?:\/\/+\s*)|(?:\/\*+\s*)|(?:^(?:\s|\*)*))((?:(TODO\(jason\))|(HACK))(?:.*?))(?:$|\*\/)/gim
3847
-
3848
- // The flags of the regexp are important here.
3849
- // 'g' is so that we are doing a global search and can find matches several times
3850
- // in the input.
3851
- //
3852
- // 'i' is for case insensitivity (We do this to match C# TODO comment code).
3853
- //
3854
- // 'm' is so we can find matches in a multiline input.
3855
- return new RegExp ( regExpString , "gim" ) ;
3856
- }
3812
+ function getTodoComments ( filename : string , descriptors : TodoCommentDescriptor [ ] ) : TodoComment [ ] {
3813
+ filename = TypeScript . switchToForwardSlashes ( filename ) ;
3857
3814
3858
- function getTodoComments ( fileName : string , descriptors : TodoCommentDescriptor [ ] ) : TodoComment [ ] {
3859
- fileName = TypeScript . switchToForwardSlashes ( fileName ) ;
3815
+ var sourceFile = getCurrentSourceFile ( filename ) ;
3860
3816
3861
- var sourceFile = getCurrentSourceFile ( fileName ) ;
3862
- var syntaxTree = sourceFile . getSyntaxTree ( ) ;
3863
3817
cancellationToken . throwIfCancellationRequested ( ) ;
3864
3818
3865
- var text = syntaxTree . text ;
3866
- var fileContents = text . substr ( 0 , text . length ( ) ) ;
3819
+ var fileContents = sourceFile . text ;
3820
+
3867
3821
cancellationToken . throwIfCancellationRequested ( ) ;
3868
3822
3869
3823
var result : TodoComment [ ] = [ ] ;
3870
3824
3871
3825
if ( descriptors . length > 0 ) {
3872
- var regExp = getTodoCommentsRegExp ( descriptors ) ;
3826
+ var regExp = getTodoCommentsRegExp ( ) ;
3873
3827
3874
3828
var matchArray : RegExpExecArray ;
3875
3829
while ( matchArray = regExp . exec ( fileContents ) ) {
@@ -3884,7 +3838,7 @@ module ts {
3884
3838
// ["// hack 1", "// ", "hack 1", undefined, "hack"]
3885
3839
//
3886
3840
// Here are the relevant capture groups:
3887
- // 0) The full match for the entire regex .
3841
+ // 0) The full match for the entire regexp .
3888
3842
// 1) The preamble to the message portion.
3889
3843
// 2) The message portion.
3890
3844
// 3...N) The descriptor that was matched - by index. 'undefined' for each
@@ -3898,20 +3852,19 @@ module ts {
3898
3852
var preamble = matchArray [ 1 ] ;
3899
3853
var matchPosition = matchArray . index + preamble . length ;
3900
3854
3901
- // Ok , we have found a match in the file. This is only an acceptable match if
3855
+ // OK , we have found a match in the file. This is only an acceptable match if
3902
3856
// it is contained within a comment.
3903
- var token = TypeScript . findToken ( syntaxTree . sourceUnit ( ) , matchPosition ) ;
3857
+ var token = getTokenAtPosition ( sourceFile , matchPosition ) ;
3904
3858
3905
- if ( matchPosition >= TypeScript . start ( token ) && matchPosition < TypeScript . end ( token ) ) {
3859
+ if ( token . getStart ( ) <= matchPosition && matchPosition < token . getEnd ( ) ) {
3906
3860
// match was within the token itself. Not in the comment. Keep searching
3907
3861
// descriptor.
3908
3862
continue ;
3909
3863
}
3910
3864
3911
- // Looks to be within the trivia. See if we can find the comment containing it.
3912
- var triviaList = matchPosition < TypeScript . start ( token ) ? token . leadingTrivia ( syntaxTree . text ) : token . trailingTrivia ( syntaxTree . text ) ;
3913
- var trivia = findContainingComment ( triviaList , matchPosition ) ;
3914
- if ( trivia === null ) {
3865
+ // Looks to be within the trivia. See if we can find the comment containing it.
3866
+ if ( ! getContainingComment ( getTrailingComments ( fileContents , token . getFullStart ( ) ) , matchPosition ) &&
3867
+ ! getContainingComment ( getLeadingComments ( fileContents , token . getFullStart ( ) ) , matchPosition ) ) {
3915
3868
continue ;
3916
3869
}
3917
3870
@@ -3935,25 +3888,89 @@ module ts {
3935
3888
}
3936
3889
3937
3890
return result ;
3938
- }
3939
3891
3940
- function isLetterOrDigit ( char : number ) : boolean {
3941
- return ( char >= TypeScript . CharacterCodes . a && char <= TypeScript . CharacterCodes . z ) ||
3942
- ( char >= TypeScript . CharacterCodes . A && char <= TypeScript . CharacterCodes . Z ) ||
3943
- ( char >= TypeScript . CharacterCodes . _0 && char <= TypeScript . CharacterCodes . _9 ) ;
3944
- }
3945
-
3946
- function findContainingComment ( triviaList : TypeScript . ISyntaxTriviaList , position : number ) : TypeScript . ISyntaxTrivia {
3947
- for ( var i = 0 , n = triviaList . count ( ) ; i < n ; i ++ ) {
3948
- var trivia = triviaList . syntaxTriviaAt ( i ) ;
3949
- var fullEnd = trivia . fullStart ( ) + trivia . fullWidth ( ) ;
3950
- if ( trivia . isComment ( ) && trivia . fullStart ( ) <= position && position < fullEnd ) {
3951
- return trivia ;
3892
+ function escapeRegExp ( str : string ) : string {
3893
+ return str . replace ( / [ \- \[ \] \/ \{ \} \( \) \* \+ \? \. \\ \^ \$ \| ] / g, "\\$&" ) ;
3894
+ }
3895
+
3896
+ function getTodoCommentsRegExp ( ) : RegExp {
3897
+ // NOTE: ?: means 'non-capture group'. It allows us to have groups without having to
3898
+ // filter them out later in the final result array.
3899
+
3900
+ // TODO comments can appear in one of the following forms:
3901
+ //
3902
+ // 1) // TODO or /////////// TODO
3903
+ //
3904
+ // 2) /* TODO or /********** TODO
3905
+ //
3906
+ // 3) /*
3907
+ // * TODO
3908
+ // */
3909
+ //
3910
+ // The following three regexps are used to match the start of the text up to the TODO
3911
+ // comment portion.
3912
+ var singleLineCommentStart = / (?: \/ \/ + \s * ) / . source ;
3913
+ var multiLineCommentStart = / (?: \/ \* + \s * ) / . source ;
3914
+ var anyNumberOfSpacesAndAsterixesAtStartOfLine = / (?: ^ (?: \s | \* ) * ) / . source ;
3915
+
3916
+ // Match any of the above three TODO comment start regexps.
3917
+ // Note that the outermost group *is* a capture group. We want to capture the preamble
3918
+ // so that we can determine the starting position of the TODO comment match.
3919
+ var preamble = "(" + anyNumberOfSpacesAndAsterixesAtStartOfLine + "|" + singleLineCommentStart + "|" + multiLineCommentStart + ")" ;
3920
+
3921
+ // Takes the descriptors and forms a regexp that matches them as if they were literals.
3922
+ // For example, if the descriptors are "TODO(jason)" and "HACK", then this will be:
3923
+ //
3924
+ // (?:(TODO\(jason\))|(HACK))
3925
+ //
3926
+ // Note that the outermost group is *not* a capture group, but the innermost groups
3927
+ // *are* capture groups. By capturing the inner literals we can determine after
3928
+ // matching which descriptor we are dealing with.
3929
+ var literals = "(?:" + map ( descriptors , d => "(" + escapeRegExp ( d . text ) + ")" ) . join ( "|" ) + ")" ;
3930
+
3931
+ // After matching a descriptor literal, the following regexp matches the rest of the
3932
+ // text up to the end of the line (or */).
3933
+ var endOfLineOrEndOfComment = / (?: $ | \* \/ ) / . source
3934
+ var messageRemainder = / (?: .* ?) / . source
3935
+
3936
+ // This is the portion of the match we'll return as part of the TODO comment result. We
3937
+ // match the literal portion up to the end of the line or end of comment.
3938
+ var messagePortion = "(" + literals + messageRemainder + ")" ;
3939
+ var regExpString = preamble + messagePortion + endOfLineOrEndOfComment ;
3940
+
3941
+ // The final regexp will look like this:
3942
+ // /((?:\/\/+\s*)|(?:\/\*+\s*)|(?:^(?:\s|\*)*))((?:(TODO\(jason\))|(HACK))(?:.*?))(?:$|\*\/)/gim
3943
+
3944
+ // The flags of the regexp are important here.
3945
+ // 'g' is so that we are doing a global search and can find matches several times
3946
+ // in the input.
3947
+ //
3948
+ // 'i' is for case insensitivity (We do this to match C# TODO comment code).
3949
+ //
3950
+ // 'm' is so we can find matches in a multi-line input.
3951
+ return new RegExp ( regExpString , "gim" ) ;
3952
+ }
3953
+
3954
+ function getContainingComment ( comments : Comment [ ] , position : number ) : Comment {
3955
+ if ( comments ) {
3956
+ for ( var i = 0 , n = comments . length ; i < n ; i ++ ) {
3957
+ var comment = comments [ i ] ;
3958
+ if ( comment . pos <= position && position < comment . end ) {
3959
+ return comment ;
3960
+ }
3961
+ }
3952
3962
}
3963
+
3964
+ return undefined ;
3953
3965
}
3954
3966
3955
- return null ;
3967
+ function isLetterOrDigit ( char : number ) : boolean {
3968
+ return ( char >= TypeScript . CharacterCodes . a && char <= TypeScript . CharacterCodes . z ) ||
3969
+ ( char >= TypeScript . CharacterCodes . A && char <= TypeScript . CharacterCodes . Z ) ||
3970
+ ( char >= TypeScript . CharacterCodes . _0 && char <= TypeScript . CharacterCodes . _9 ) ;
3971
+ }
3956
3972
}
3973
+
3957
3974
3958
3975
function getRenameInfo ( fileName : string , position : number ) : RenameInfo {
3959
3976
synchronizeHostData ( ) ;
0 commit comments