blob: ab774d182cbbabb97fcd7f75106c122f1cf5a419 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.cache.impl;
import com.intellij.lexer.DelegateLexer;
import com.intellij.lexer.Lexer;
import com.intellij.psi.impl.cache.impl.id.IdTableBuilding;
import com.intellij.psi.search.IndexPattern;
import com.intellij.psi.search.UsageSearchContext;
import com.intellij.util.text.CharArrayUtil;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class BaseFilterLexer extends DelegateLexer implements IdTableBuilding.ScanWordProcessor {
private final OccurrenceConsumer myOccurrenceConsumer;
private int myTodoScannedBound = 0;
private int myOccurenceMask;
private TodoScanningState myTodoScanningState;
private CharSequence myCachedBufferSequence;
private char[] myCachedArraySequence;
protected BaseFilterLexer(Lexer originalLexer, OccurrenceConsumer occurrenceConsumer) {
super(originalLexer);
myOccurrenceConsumer = occurrenceConsumer;
}
protected final void advanceTodoItemCountsInToken() {
if (!myOccurrenceConsumer.isNeedToDo()) return;
int start = getTokenStart();
int end = getTokenEnd();
start = Math.max(start, myTodoScannedBound);
if (start >= end) return; // this prevents scanning of the same comment twice
CharSequence input = myCachedBufferSequence.subSequence(start, end);
myTodoScanningState = advanceTodoItemsCount(input, myOccurrenceConsumer, myTodoScanningState);
myTodoScannedBound = end;
}
public static class TodoScanningState {
final IndexPattern[] myPatterns;
final Matcher[] myMatchers;
TIntArrayList myOccurences;
public TodoScanningState(IndexPattern[] patterns, Matcher[] matchers) {
myPatterns = patterns;
myMatchers = matchers;
myOccurences = new TIntArrayList(1);
}
}
public static TodoScanningState advanceTodoItemsCount(final CharSequence input, final OccurrenceConsumer consumer, TodoScanningState todoScanningState) {
if (todoScanningState == null) {
IndexPattern[] patterns = IndexPatternUtil.getIndexPatterns();
Matcher[] matchers = new Matcher[patterns.length];
todoScanningState = new TodoScanningState(patterns, matchers);
for (int i = 0; i < patterns.length; ++i) {
Pattern pattern = patterns[i].getOptimizedIndexingPattern();
if (pattern != null) {
matchers[i] = pattern.matcher("");
}
}
} else {
todoScanningState.myOccurences.resetQuick();
}
for (int i = todoScanningState.myMatchers.length - 1; i >= 0; --i) {
Matcher matcher = todoScanningState.myMatchers[i];
if (matcher == null) continue;
matcher.reset(input);
while (matcher.find()) {
int start = matcher.start();
if (start != matcher.end() && todoScanningState.myOccurences.indexOf(start) == -1) {
consumer.incTodoOccurrence(todoScanningState.myPatterns[i]);
todoScanningState.myOccurences.add(start);
}
}
}
return todoScanningState;
}
@Override
public final void run(CharSequence chars, @Nullable char[] charsArray, int start, int end) {
myOccurrenceConsumer.addOccurrence(chars, charsArray, start, end, myOccurenceMask);
}
protected final void addOccurrenceInToken(final int occurrenceMask) {
myOccurrenceConsumer.addOccurrence(myCachedBufferSequence, myCachedArraySequence, getTokenStart(), getTokenEnd(), occurrenceMask);
}
protected final void scanWordsInToken(final int occurrenceMask, boolean mayHaveFileRefs, final boolean mayHaveEscapes) {
myOccurenceMask = occurrenceMask;
final int start = getTokenStart();
final int end = getTokenEnd();
IdTableBuilding.scanWords(this, myCachedBufferSequence, myCachedArraySequence, start, end, mayHaveEscapes);
if (mayHaveFileRefs) {
processPossibleComplexFileName(myCachedBufferSequence, myCachedArraySequence, start, end);
}
}
private void processPossibleComplexFileName(CharSequence chars, char[] cachedArraySequence, int startOffset, int endOffset) {
int offset = findCharsWithinRange(chars, startOffset, endOffset, "/\\");
offset = Math.min(offset, endOffset);
int start = startOffset;
while(start < endOffset) {
if (start != offset) {
myOccurrenceConsumer.addOccurrence(chars, cachedArraySequence, start, offset, UsageSearchContext.IN_FOREIGN_LANGUAGES);
}
start = offset + 1;
offset = Math.min(endOffset, findCharsWithinRange(chars, start, endOffset, "/\\"));
}
}
private static int findCharsWithinRange(final CharSequence chars, int startOffset, int endOffset, String charsToFind) {
while(startOffset < endOffset) {
if (charsToFind.indexOf(chars.charAt(startOffset)) != -1) {
return startOffset;
}
++startOffset;
}
return startOffset;
}
@Override
public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) {
super.start(buffer, startOffset, endOffset, initialState);
myCachedBufferSequence = getBufferSequence();
myCachedArraySequence = CharArrayUtil.fromSequenceWithoutCopying(myCachedBufferSequence);
}
}