2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com
.intellij
.psi
.impl
.search
;
19 import com
.intellij
.lang
.Language
;
20 import com
.intellij
.lang
.LanguageParserDefinitions
;
21 import com
.intellij
.lang
.ParserDefinition
;
22 import com
.intellij
.lexer
.Lexer
;
23 import com
.intellij
.openapi
.extensions
.Extensions
;
24 import com
.intellij
.openapi
.fileTypes
.FileType
;
25 import com
.intellij
.openapi
.fileTypes
.SyntaxHighlighter
;
26 import com
.intellij
.openapi
.fileTypes
.SyntaxHighlighterFactory
;
27 import com
.intellij
.openapi
.fileTypes
.impl
.AbstractFileType
;
28 import com
.intellij
.openapi
.progress
.ProgressManager
;
29 import com
.intellij
.openapi
.util
.TextRange
;
30 import com
.intellij
.openapi
.vfs
.VirtualFile
;
31 import com
.intellij
.psi
.*;
32 import com
.intellij
.psi
.impl
.PsiManagerEx
;
33 import com
.intellij
.psi
.impl
.cache
.CacheManager
;
34 import com
.intellij
.psi
.search
.IndexPattern
;
35 import com
.intellij
.psi
.search
.IndexPatternOccurrence
;
36 import com
.intellij
.psi
.search
.IndexPatternProvider
;
37 import com
.intellij
.psi
.search
.searches
.IndexPatternSearch
;
38 import com
.intellij
.psi
.tree
.IElementType
;
39 import com
.intellij
.psi
.tree
.TokenSet
;
40 import com
.intellij
.util
.Processor
;
41 import com
.intellij
.util
.QueryExecutor
;
42 import com
.intellij
.util
.text
.CharSequenceSubSequence
;
43 import gnu
.trove
.TIntArrayList
;
45 import java
.util
.regex
.Matcher
;
46 import java
.util
.regex
.Pattern
;
51 public class IndexPatternSearcher
implements QueryExecutor
<IndexPatternOccurrence
, IndexPatternSearch
.SearchParameters
> {
52 public boolean execute(final IndexPatternSearch
.SearchParameters queryParameters
, final Processor
<IndexPatternOccurrence
> consumer
) {
53 final PsiFile file
= queryParameters
.getFile();
54 VirtualFile virtualFile
= file
.getVirtualFile();
55 if (file
instanceof PsiBinaryFile
|| file
instanceof PsiCompiledElement
|| virtualFile
== null) {
59 final CacheManager cacheManager
= ((PsiManagerEx
)file
.getManager()).getCacheManager();
60 final IndexPatternProvider patternProvider
= queryParameters
.getPatternProvider();
61 int count
= patternProvider
!= null ? cacheManager
.getTodoCount(virtualFile
, patternProvider
) : cacheManager
.getTodoCount(virtualFile
, queryParameters
.getPattern());
62 if (count
== 0) return true;
64 TIntArrayList commentStarts
= new TIntArrayList();
65 TIntArrayList commentEnds
= new TIntArrayList();
67 final CharSequence chars
= file
.getViewProvider().getContents();
68 findCommentTokenRanges(file
, chars
, queryParameters
.getRange(), commentStarts
, commentEnds
);
70 for (int i
= 0; i
< commentStarts
.size(); i
++) {
71 int commentStart
= commentStarts
.get(i
);
72 int commentEnd
= commentEnds
.get(i
);
74 if (patternProvider
!= null) {
75 for(final IndexPattern pattern
: patternProvider
.getIndexPatterns()) {
76 if (!collectPatternMatches(pattern
, chars
, commentStart
, commentEnd
, file
, queryParameters
.getRange(), consumer
)) {
82 if (!collectPatternMatches(queryParameters
.getPattern(), chars
, commentStart
, commentEnd
, file
, queryParameters
.getRange(), consumer
)) {
92 private static final TokenSet COMMENT_TOKENS
= TokenSet
.create(CustomHighlighterTokenType
.LINE_COMMENT
, CustomHighlighterTokenType
.MULTI_LINE_COMMENT
);
94 private static void findCommentTokenRanges(final PsiFile file
,
95 final CharSequence chars
,
96 final TextRange range
,
97 final TIntArrayList commentStarts
,
98 final TIntArrayList commentEnds
) {
99 if (file
instanceof PsiPlainTextFile
) {
100 FileType fType
= file
.getFileType();
101 synchronized (PsiLock
.LOCK
) {
102 if (fType
instanceof AbstractFileType
) {
103 Lexer lexer
= SyntaxHighlighter
.PROVIDER
.create(fType
, file
.getProject(), file
.getVirtualFile()).getHighlightingLexer();
104 findComments(lexer
, chars
, range
, COMMENT_TOKENS
, commentStarts
, commentEnds
, null);
107 commentStarts
.add(0);
108 commentEnds
.add(file
.getTextLength());
113 // collect comment offsets to prevent long locks by PsiManagerImpl.LOCK
114 synchronized (PsiLock
.LOCK
) {
115 final Language lang
= file
.getLanguage();
116 final SyntaxHighlighter syntaxHighlighter
= SyntaxHighlighterFactory
.getSyntaxHighlighter(lang
, file
.getProject(), file
.getVirtualFile());
117 Lexer lexer
= syntaxHighlighter
.getHighlightingLexer();
118 TokenSet commentTokens
= null;
119 IndexPatternBuilder builderForFile
= null;
120 for(IndexPatternBuilder builder
: Extensions
.getExtensions(IndexPatternBuilder
.EP_NAME
)) {
121 Lexer lexerFromBuilder
= builder
.getIndexingLexer(file
);
122 if (lexerFromBuilder
!= null) {
123 lexer
= lexerFromBuilder
;
124 commentTokens
= builder
.getCommentTokenSet(file
);
125 builderForFile
= builder
;
128 if (builderForFile
== null) {
129 final ParserDefinition parserDefinition
= LanguageParserDefinitions
.INSTANCE
.forLanguage(lang
);
130 if (parserDefinition
!= null) {
131 commentTokens
= parserDefinition
.getCommentTokens();
135 if (commentTokens
!= null) {
136 findComments(lexer
, chars
, range
, commentTokens
, commentStarts
, commentEnds
, builderForFile
);
142 private static void findComments(final Lexer lexer
,
143 final CharSequence chars
,
144 final TextRange range
,
145 final TokenSet commentTokens
,
146 final TIntArrayList commentStarts
,
147 final TIntArrayList commentEnds
,
148 final IndexPatternBuilder builderForFile
) {
149 for (lexer
.start(chars
); ; lexer
.advance()) {
150 IElementType tokenType
= lexer
.getTokenType();
151 if (tokenType
== null) break;
154 if (lexer
.getTokenEnd() <= range
.getStartOffset()) continue;
155 if (lexer
.getTokenStart() >= range
.getEndOffset()) break;
158 boolean isComment
= commentTokens
.contains(tokenType
);
160 final Language commentLang
= tokenType
.getLanguage();
161 final ParserDefinition parserDefinition
= LanguageParserDefinitions
.INSTANCE
.forLanguage(commentLang
);
162 if (parserDefinition
!= null) {
163 final TokenSet langCommentTokens
= parserDefinition
.getCommentTokens();
164 isComment
= langCommentTokens
.contains(tokenType
);
169 final int startDelta
= builderForFile
!= null ? builderForFile
.getCommentStartDelta(lexer
.getTokenType()) : 0;
170 final int endDelta
= builderForFile
!= null ? builderForFile
.getCommentEndDelta(lexer
.getTokenType()) : 0;
172 int start
= lexer
.getTokenStart() + startDelta
;
173 int end
= lexer
.getTokenEnd() - endDelta
;
174 assert start
<= end
: "Invalid comment range: " + new TextRange(start
, end
) + "; lexer token range="+new TextRange(lexer
.getTokenStart(), lexer
.getTokenEnd())+"; delta="+new TextRange(startDelta
, endDelta
)+"; lexer="+lexer
+"; builder="+builderForFile
+"; chars length:"+chars
.length();
175 assert end
<= chars
.length(): "Invalid comment end: " + new TextRange(start
, end
) + "; lexer token range="+new TextRange(lexer
.getTokenStart(), lexer
.getTokenEnd())+"; delta="+new TextRange(startDelta
, endDelta
)+"; lexer="+lexer
+"; builder="+builderForFile
+"; chars length:"+chars
.length();
176 commentStarts
.add(start
);
177 commentEnds
.add(end
);
182 private static boolean collectPatternMatches(IndexPattern indexPattern
,
188 Processor
<IndexPatternOccurrence
> consumer
) {
189 Pattern pattern
= indexPattern
.getPattern();
190 if (pattern
!= null) {
191 ProgressManager
.checkCanceled();
193 CharSequence input
= new CharSequenceSubSequence(chars
, commentStart
, commentEnd
);
194 Matcher matcher
= pattern
.matcher(input
);
196 //long time1 = System.currentTimeMillis();
197 boolean found
= matcher
.find();
198 //long time2 = System.currentTimeMillis();
199 //System.out.println("scanned text of length " + (lexer.getTokenEnd() - lexer.getTokenStart() + " in " + (time2 - time1) + " ms"));
202 int start
= matcher
.start() + commentStart
;
203 int end
= matcher
.end() + commentStart
;
205 if (range
== null || range
.getStartOffset() <= start
&& end
<= range
.getEndOffset()) {
206 if (!consumer
.process(new IndexPatternOccurrenceImpl(file
, start
, end
, indexPattern
))) {
212 ProgressManager
.checkCanceled();