test isolation
[fedora-idea.git] / platform / lang-impl / src / com / intellij / psi / impl / search / IndexPatternSearcher.java
blob5d7a2b58d838cf531d0e9b94734c6fc3960d1e1c
1 /*
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;
48 /**
49 * @author yole
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) {
56 return true;
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)) {
77 return false;
81 else {
82 if (!collectPatternMatches(queryParameters.getPattern(), chars, commentStart, commentEnd, file, queryParameters.getRange(), consumer)) {
83 return false;
88 return true;
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);
106 else {
107 commentStarts.add(0);
108 commentEnds.add(file.getTextLength());
112 else {
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;
153 if (range != null) {
154 if (lexer.getTokenEnd() <= range.getStartOffset()) continue;
155 if (lexer.getTokenStart() >= range.getEndOffset()) break;
158 boolean isComment = commentTokens.contains(tokenType);
159 if (!isComment) {
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);
168 if (isComment) {
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,
183 CharSequence chars,
184 int commentStart,
185 int commentEnd,
186 PsiFile file,
187 TextRange range,
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);
195 while (true) {
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"));
201 if (!found) break;
202 int start = matcher.start() + commentStart;
203 int end = matcher.end() + commentStart;
204 if (start != end) {
205 if (range == null || range.getStartOffset() <= start && end <= range.getEndOffset()) {
206 if (!consumer.process(new IndexPatternOccurrenceImpl(file, start, end, indexPattern))) {
207 return false;
212 ProgressManager.checkCanceled();
215 return true;