blob: eb64dcff475f3a5cacce5bdfee64e10ccb127e04 [file] [log] [blame]
/*
* Copyright 2000-2012 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.id;
import com.intellij.ide.highlighter.custom.CustomFileTypeLexer;
import com.intellij.ide.highlighter.custom.SyntaxTable;
import com.intellij.lang.Language;
import com.intellij.lang.cacheBuilder.*;
import com.intellij.lang.findUsages.FindUsagesProvider;
import com.intellij.lang.findUsages.LanguageFindUsages;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.InternalFileType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.fileTypes.impl.CustomSyntaxTableFileType;
import com.intellij.psi.CustomHighlighterTokenType;
import com.intellij.psi.search.UsageSearchContext;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.Processor;
import com.intellij.util.indexing.FileContent;
import com.intellij.util.indexing.IdDataConsumer;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
public class IdTableBuilding {
private IdTableBuilding() {
}
public interface ScanWordProcessor {
void run(CharSequence chars, @Nullable char[] charsArray, int start, int end);
}
public static class PlainTextIndexer extends FileTypeIdIndexer {
@Override
@NotNull
public Map<IdIndexEntry, Integer> map(final FileContent inputData) {
final IdDataConsumer consumer = new IdDataConsumer();
final CharSequence chars = inputData.getContentAsText();
scanWords(new ScanWordProcessor() {
@Override
public void run(final CharSequence chars11, @Nullable char[] charsArray, final int start, final int end) {
if (charsArray != null) {
consumer.addOccurrence(charsArray, start, end, (int)UsageSearchContext.IN_PLAIN_TEXT);
} else {
consumer.addOccurrence(chars11, start, end, (int)UsageSearchContext.IN_PLAIN_TEXT);
}
}
}, chars, 0, chars.length());
return consumer.getResult();
}
}
private static final Map<FileType, FileTypeIdIndexer> ourIdIndexers = new HashMap<FileType, FileTypeIdIndexer>();
@Deprecated
public static void registerIdIndexer(@NotNull FileType fileType, FileTypeIdIndexer indexer) {
ourIdIndexers.put(fileType, indexer);
}
public static boolean isIdIndexerRegistered(@NotNull FileType fileType) {
return ourIdIndexers.containsKey(fileType) || IdIndexers.INSTANCE.forFileType(fileType) != null || fileType instanceof InternalFileType;
}
@Nullable
public static FileTypeIdIndexer getFileTypeIndexer(FileType fileType) {
final FileTypeIdIndexer idIndexer = ourIdIndexers.get(fileType);
if (idIndexer != null) {
return idIndexer;
}
final FileTypeIdIndexer extIndexer = IdIndexers.INSTANCE.forFileType(fileType);
if (extIndexer != null) {
return extIndexer;
}
final WordsScanner customWordsScanner = CacheBuilderRegistry.getInstance().getCacheBuilder(fileType);
if (customWordsScanner != null) {
return new WordsScannerFileTypeIdIndexerAdapter(customWordsScanner);
}
if (fileType instanceof LanguageFileType) {
final Language lang = ((LanguageFileType)fileType).getLanguage();
final FindUsagesProvider findUsagesProvider = LanguageFindUsages.INSTANCE.forLanguage(lang);
WordsScanner scanner = findUsagesProvider == null ? null : findUsagesProvider.getWordsScanner();
if (scanner == null) {
scanner = new SimpleWordsScanner();
}
return new WordsScannerFileTypeIdIndexerAdapter(scanner);
}
if (fileType instanceof CustomSyntaxTableFileType) {
return new WordsScannerFileTypeIdIndexerAdapter(createCustomFileTypeScanner(((CustomSyntaxTableFileType)fileType).getSyntaxTable()));
}
return null;
}
public static WordsScanner createCustomFileTypeScanner(SyntaxTable syntaxTable) {
return new DefaultWordsScanner(new CustomFileTypeLexer(syntaxTable, true),
TokenSet.create(CustomHighlighterTokenType.IDENTIFIER),
TokenSet.create(CustomHighlighterTokenType.LINE_COMMENT,
CustomHighlighterTokenType.MULTI_LINE_COMMENT),
TokenSet.create(CustomHighlighterTokenType.STRING, CustomHighlighterTokenType.SINGLE_QUOTED_STRING));
}
private static class WordsScannerFileTypeIdIndexerAdapter extends FileTypeIdIndexer {
private final WordsScanner myScanner;
public WordsScannerFileTypeIdIndexerAdapter(@NotNull final WordsScanner scanner) {
myScanner = scanner;
}
@Override
@NotNull
public Map<IdIndexEntry, Integer> map(final FileContent inputData) {
final CharSequence chars = inputData.getContentAsText();
final char[] charsArray = CharArrayUtil.fromSequenceWithoutCopying(chars);
final IdDataConsumer consumer = new IdDataConsumer();
myScanner.processWords(chars, new Processor<WordOccurrence>() {
@Override
public boolean process(final WordOccurrence t) {
if (charsArray != null && t.getBaseText() == chars) {
consumer.addOccurrence(charsArray, t.getStart(), t.getEnd(), convertToMask(t.getKind()));
}
else {
consumer.addOccurrence(t.getBaseText(), t.getStart(), t.getEnd(), convertToMask(t.getKind()));
}
return true;
}
private int convertToMask(final WordOccurrence.Kind kind) {
if (kind == null) return UsageSearchContext.ANY;
if (kind == WordOccurrence.Kind.CODE) return UsageSearchContext.IN_CODE;
if (kind == WordOccurrence.Kind.COMMENTS) return UsageSearchContext.IN_COMMENTS;
if (kind == WordOccurrence.Kind.LITERALS) return UsageSearchContext.IN_STRINGS;
if (kind == WordOccurrence.Kind.FOREIGN_LANGUAGE) return UsageSearchContext.IN_FOREIGN_LANGUAGES;
return 0;
}
});
return consumer.getResult();
}
}
public static void scanWords(final ScanWordProcessor processor, final CharSequence chars, final int startOffset, final int endOffset) {
scanWords(processor, chars, CharArrayUtil.fromSequenceWithoutCopying(chars), startOffset, endOffset, false);
}
public static void scanWords(final ScanWordProcessor processor,
final CharSequence chars,
@Nullable final char[] charArray,
final int startOffset,
final int endOffset,
final boolean mayHaveEscapes) {
int index = startOffset;
final boolean hasArray = charArray != null;
ScanWordsLoop:
while (true) {
while (true) {
if (index >= endOffset) break ScanWordsLoop;
final char c = hasArray ? charArray[index] : chars.charAt(index);
if ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
(Character.isJavaIdentifierStart(c) && c != '$')) {
break;
}
index++;
if (mayHaveEscapes && c == '\\') index++; //the next symbol is for escaping
}
int index1 = index;
while (true) {
index++;
if (index >= endOffset) break;
final char c = hasArray ? charArray[index] : chars.charAt(index);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) continue;
if (!Character.isJavaIdentifierPart(c) || c == '$') break;
}
if (index - index1 > 100) continue; // Strange limit but we should have some!
processor.run(chars, charArray, index1, index);
}
}
}