mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
huge number of text usages per file support (multiple RA & singlepass lexing)
This commit is contained in:
@@ -85,6 +85,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
public class FindInProjectUtil {
|
||||
private static final int USAGES_LIMIT = 1000;
|
||||
private static final int USAGES_PER_READ_ACTION = 100;
|
||||
private static final int FILES_SIZE_LIMIT = 70 * 1024 * 1024; // megabytes.
|
||||
private static final int SINGLE_FILE_SIZE_LIMIT = 5 * 1024 * 1024; // megabytes.
|
||||
|
||||
@@ -218,7 +219,7 @@ public class FindInProjectUtil {
|
||||
int i = 0;
|
||||
long totalFilesSize = 0;
|
||||
int count = 0;
|
||||
boolean warningShown = false;
|
||||
final boolean[] warningShown = new boolean[] {false};
|
||||
|
||||
final UsageViewManager usageViewManager = UsageViewManager.getInstance(project);
|
||||
for (final PsiFile psiFile : psiFiles) {
|
||||
@@ -252,22 +253,16 @@ public class FindInProjectUtil {
|
||||
progress.setText2(FindBundle.message("find.searching.for.string.in.file.occurrences.progress", count));
|
||||
}
|
||||
|
||||
int countInFile = processUsagesInFile(psiFile, findModel, consumer);
|
||||
int countInFile = processUsagesInFile(psiFile, findModel, consumer, count, warningShown);
|
||||
|
||||
if (countInFile > 0) {
|
||||
totalFilesSize += fileLength;
|
||||
if (totalFilesSize > FILES_SIZE_LIMIT && !warningShown) {
|
||||
if (totalFilesSize > FILES_SIZE_LIMIT && !warningShown[0]) {
|
||||
showTooManyUsagesWaring(project, FindBundle.message("find.excessive.total.size.prompt", presentableSize(totalFilesSize),
|
||||
ApplicationNamesInfo.getInstance().getProductName()));
|
||||
warningShown = true;
|
||||
warningShown[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
count += countInFile;
|
||||
if (count > USAGES_LIMIT && !warningShown) {
|
||||
showTooManyUsagesWaring(project, FindBundle.message("find.excessive.usage.count.prompt", count));
|
||||
warningShown = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!largeFiles.isEmpty()) {
|
||||
@@ -317,22 +312,37 @@ public class FindInProjectUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static int processUsagesInFile(final PsiFile psiFile, final FindModel findModel, final Processor<UsageInfo> consumer) {
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
|
||||
public Integer compute() {
|
||||
if (psiFile.isValid()) {
|
||||
VirtualFile virtualFile = psiFile.getVirtualFile();
|
||||
// Check once more if valid and text since we're in new read action and things might have been changed.
|
||||
if (FileTypeManager.getInstance().getFileTypeByFile(virtualFile).isBinary()) return 0; // do not decompile .class files
|
||||
final Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
|
||||
if (document != null) {
|
||||
return addToUsages(document, consumer, findModel, psiFile);
|
||||
}
|
||||
private static int processUsagesInFile(final PsiFile psiFile,
|
||||
final FindModel findModel,
|
||||
final Processor<UsageInfo> consumer,
|
||||
int alreadyCounted,
|
||||
boolean[] warningShown) {
|
||||
final VirtualFile virtualFile = psiFile.getVirtualFile();
|
||||
if (virtualFile == null) return 0;
|
||||
if (FileTypeManager.getInstance().getFileTypeByFile(virtualFile).isBinary()) return 0; // do not decompile .class files
|
||||
final Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
|
||||
if (document == null) return 0;
|
||||
final int[] offset = new int[] {0};
|
||||
int count = 0;
|
||||
int found;
|
||||
do {
|
||||
found = ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
|
||||
@Override
|
||||
@NotNull
|
||||
public Integer compute() {
|
||||
if (!psiFile.isValid()) return 0;
|
||||
return addToUsages(document, consumer, findModel, psiFile, offset, USAGES_PER_READ_ACTION);
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
count += found;
|
||||
if (found > 0 && count + alreadyCounted > USAGES_LIMIT && !warningShown[0]) {
|
||||
showTooManyUsagesWaring(psiFile.getProject(), FindBundle.message("find.excessive.usage.count.prompt", count));
|
||||
warningShown[0] = true;
|
||||
}
|
||||
}).intValue();
|
||||
|
||||
}
|
||||
while (found != 0);
|
||||
return count;
|
||||
}
|
||||
|
||||
private static String getPresentablePath(final VirtualFile virtualFile) {
|
||||
@@ -605,12 +615,11 @@ public class FindInProjectUtil {
|
||||
}
|
||||
|
||||
private static int addToUsages(@NotNull Document document, @NotNull Processor<UsageInfo> consumer, @NotNull FindModel findModel,
|
||||
@NotNull final PsiFile psiFile) {
|
||||
|
||||
@NotNull final PsiFile psiFile, int[] offsetRef, int maxUsages) {
|
||||
int count = 0;
|
||||
CharSequence text = document.getCharsSequence();
|
||||
int textLength = document.getTextLength();
|
||||
int offset = 0;
|
||||
int offset = offsetRef[0];
|
||||
|
||||
Project project = psiFile.getProject();
|
||||
|
||||
@@ -632,7 +641,11 @@ public class FindInProjectUtil {
|
||||
// for regular expr the size of the match could be zero -> could be infinite loop in finding usages!
|
||||
++offset;
|
||||
}
|
||||
if (maxUsages > 0 && count >= maxUsages) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
offsetRef[0] = offset;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,14 +29,19 @@ import com.intellij.openapi.fileTypes.SyntaxHighlighter;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.usageView.UsageTreeColors;
|
||||
import com.intellij.usageView.UsageTreeColorsScheme;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
@@ -44,29 +49,65 @@ import java.util.List;
|
||||
public class ChunkExtractor {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.usages.ChunkExtractor");
|
||||
|
||||
private final PsiElement myElement;
|
||||
private final Document myDocument;
|
||||
private final int myLineNumber;
|
||||
private final int myColumnNumber;
|
||||
|
||||
private final List<RangeMarker> myRangeMarkers;
|
||||
private final EditorColorsScheme myColorsScheme;
|
||||
|
||||
public ChunkExtractor(final PsiElement element, final List<RangeMarker> rangeMarkers) {
|
||||
myElement = element;
|
||||
myRangeMarkers = new ArrayList<RangeMarker>(rangeMarkers.size());
|
||||
for (RangeMarker rangeMarker : rangeMarkers) {
|
||||
if (rangeMarker.isValid()) myRangeMarkers.add(rangeMarker);
|
||||
private final Document myDocument;
|
||||
private final SyntaxHighlighter myHighlighter;
|
||||
|
||||
private final Lexer myLexer;
|
||||
|
||||
private static abstract class WeakFactory<T> {
|
||||
|
||||
private WeakReference<T> myRef;
|
||||
|
||||
@NotNull
|
||||
protected abstract T create();
|
||||
|
||||
@NotNull
|
||||
public T getValue() {
|
||||
final T cur = myRef == null ? null : myRef.get();
|
||||
if (cur != null) return cur;
|
||||
final T result = create();
|
||||
myRef = new WeakReference<T>(result);
|
||||
return result;
|
||||
}
|
||||
Collections.sort(myRangeMarkers, RangeMarker.BY_START_OFFSET);
|
||||
|
||||
}
|
||||
|
||||
private static final ThreadLocal<WeakFactory<Map<PsiFile, ChunkExtractor>>> ourExtractors = new ThreadLocal<WeakFactory<Map<PsiFile, ChunkExtractor>>>() {
|
||||
@Override
|
||||
protected WeakFactory<Map<PsiFile, ChunkExtractor>> initialValue() {
|
||||
return new WeakFactory<Map<PsiFile, ChunkExtractor>>() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected Map<PsiFile, ChunkExtractor> create() {
|
||||
return new FactoryMap<PsiFile, ChunkExtractor>() {
|
||||
@Override
|
||||
protected ChunkExtractor create(PsiFile key) {
|
||||
return new ChunkExtractor(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
public static TextChunk[] extractChunks(PsiElement element, List<RangeMarker> rangeMarkers) {
|
||||
return ourExtractors.get().getValue().get(element.getContainingFile()).extractChunks(rangeMarkers);
|
||||
}
|
||||
|
||||
|
||||
private ChunkExtractor(PsiFile file) {
|
||||
myColorsScheme = UsageTreeColorsScheme.getInstance().getScheme();
|
||||
|
||||
int absoluteStartOffset = getStartOffset(myRangeMarkers);
|
||||
assert absoluteStartOffset != -1;
|
||||
|
||||
myDocument = PsiDocumentManager.getInstance(myElement.getProject()).getDocument(myElement.getContainingFile());
|
||||
myLineNumber = myDocument.getLineNumber(absoluteStartOffset);
|
||||
myColumnNumber = absoluteStartOffset - myDocument.getLineStartOffset(myLineNumber);
|
||||
myDocument = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
|
||||
LOG.assertTrue(myDocument != null);
|
||||
final FileType fileType = file.getFileType();
|
||||
final SyntaxHighlighter highlighter =
|
||||
SyntaxHighlighter.PROVIDER.create(fileType, file.getProject(), file.getVirtualFile());
|
||||
myHighlighter = highlighter == null ? new PlainSyntaxHighlighter() : highlighter;
|
||||
myLexer = myHighlighter.getHighlightingLexer();
|
||||
myLexer.start(myDocument.getCharsSequence());
|
||||
}
|
||||
|
||||
public static int getStartOffset(final List<RangeMarker> rangeMarkers) {
|
||||
@@ -80,30 +121,40 @@ public class ChunkExtractor {
|
||||
return minStart == Integer.MAX_VALUE ? -1 : minStart;
|
||||
}
|
||||
|
||||
public TextChunk[] extractChunks() {
|
||||
final int lineStartOffset = myDocument.getLineStartOffset(myLineNumber);
|
||||
final int lineEndOffset = lineStartOffset < myDocument.getTextLength() ? myDocument.getLineEndOffset(myLineNumber):0;
|
||||
if (lineStartOffset > lineEndOffset) return new TextChunk[0];
|
||||
final FileType fileType = myElement.getContainingFile().getFileType();
|
||||
SyntaxHighlighter highlighter =
|
||||
SyntaxHighlighter.PROVIDER.create(fileType, myElement.getProject(), myElement.getContainingFile().getVirtualFile());
|
||||
if (highlighter == null) {
|
||||
highlighter = new PlainSyntaxHighlighter();
|
||||
public TextChunk[] extractChunks(List<RangeMarker> rangeMarkers) {
|
||||
final ArrayList<RangeMarker> markers = new ArrayList<RangeMarker>(rangeMarkers.size());
|
||||
for (RangeMarker rangeMarker : rangeMarkers) {
|
||||
if (rangeMarker.isValid()) markers.add(rangeMarker);
|
||||
}
|
||||
return createTextChunks(myDocument.getCharsSequence(), highlighter, lineStartOffset, lineEndOffset);
|
||||
int absoluteStartOffset = getStartOffset(markers);
|
||||
assert absoluteStartOffset != -1;
|
||||
|
||||
final int lineNumber = myDocument.getLineNumber(absoluteStartOffset);
|
||||
final int columnNumber = absoluteStartOffset - myDocument.getLineStartOffset(lineNumber);
|
||||
|
||||
Collections.sort(markers, RangeMarker.BY_START_OFFSET);
|
||||
final int lineStartOffset = myDocument.getLineStartOffset(lineNumber);
|
||||
final int lineEndOffset = lineStartOffset < myDocument.getTextLength() ? myDocument.getLineEndOffset(lineNumber) : 0;
|
||||
if (lineStartOffset > lineEndOffset) return TextChunk.EMPTY_ARRAY;
|
||||
|
||||
final CharSequence chars = myDocument.getCharsSequence();
|
||||
if (myLexer.getTokenStart() > absoluteStartOffset) {
|
||||
myLexer.start(chars);
|
||||
}
|
||||
final List<TextChunk> result = new ArrayList<TextChunk>();
|
||||
appendPrefix(result, lineNumber, columnNumber);
|
||||
return createTextChunks(markers, chars, lineStartOffset, lineEndOffset, result);
|
||||
}
|
||||
|
||||
private TextChunk[] createTextChunks(final CharSequence chars,
|
||||
SyntaxHighlighter highlighter,
|
||||
private TextChunk[] createTextChunks(final List<RangeMarker> markers,
|
||||
final CharSequence chars,
|
||||
int start,
|
||||
int end) {
|
||||
int end,
|
||||
final List<TextChunk> result) {
|
||||
final Lexer lexer = myLexer;
|
||||
final SyntaxHighlighter highlighter = myHighlighter;
|
||||
|
||||
LOG.assertTrue(start <= end);
|
||||
List<TextChunk> result = new ArrayList<TextChunk>();
|
||||
|
||||
appendPrefix(result);
|
||||
|
||||
Lexer lexer = highlighter.getHighlightingLexer();
|
||||
lexer.start(chars);
|
||||
|
||||
int i = StringUtil.indexOf(chars, '\n', start, end);
|
||||
if (i != -1) end = i;
|
||||
@@ -127,7 +178,7 @@ public class ChunkExtractor {
|
||||
IElementType tokenType = lexer.getTokenType();
|
||||
TextAttributesKey[] tokenHighlights = highlighter.getTokenHighlights(tokenType);
|
||||
|
||||
processIntersectingRange(chars, hiStart, hiEnd, tokenHighlights, result);
|
||||
processIntersectingRange(markers, chars, hiStart, hiEnd, tokenHighlights, result);
|
||||
}
|
||||
finally {
|
||||
lexer.advance();
|
||||
@@ -137,14 +188,15 @@ public class ChunkExtractor {
|
||||
return result.toArray(new TextChunk[result.size()]);
|
||||
}
|
||||
|
||||
private void processIntersectingRange(CharSequence chars,
|
||||
private void processIntersectingRange(List<RangeMarker> markers,
|
||||
CharSequence chars,
|
||||
int hiStart,
|
||||
int hiEnd,
|
||||
TextAttributesKey[] tokenHighlights,
|
||||
List<TextChunk> result) {
|
||||
TextAttributes originalAttrs = convertAttributes(tokenHighlights);
|
||||
int lastOffset = hiStart;
|
||||
for(RangeMarker rangeMarker: myRangeMarkers) {
|
||||
for(RangeMarker rangeMarker: markers) {
|
||||
int usageStart = rangeMarker.getStartOffset();
|
||||
int usageEnd = rangeMarker.getEndOffset();
|
||||
if (rangeMarker.isValid() && rangeIntersect(lastOffset, hiEnd, usageStart, usageEnd)) {
|
||||
@@ -191,11 +243,11 @@ public class ChunkExtractor {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
private void appendPrefix(List<TextChunk> result) {
|
||||
private void appendPrefix(List<TextChunk> result, int lineNumber, int columnNumber) {
|
||||
StringBuilder buffer = new StringBuilder("(");
|
||||
buffer.append(myLineNumber + 1);
|
||||
buffer.append(lineNumber + 1);
|
||||
buffer.append(", ");
|
||||
buffer.append(myColumnNumber + 1);
|
||||
buffer.append(columnNumber + 1);
|
||||
buffer.append(") ");
|
||||
TextChunk prefixChunk = new TextChunk(myColorsScheme.getAttributes(UsageTreeColors.USAGE_LOCATION), buffer.toString());
|
||||
result.add(prefixChunk);
|
||||
|
||||
@@ -20,6 +20,9 @@ import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class TextChunk {
|
||||
|
||||
public static final TextChunk[] EMPTY_ARRAY = new TextChunk[0];
|
||||
|
||||
private final AttributesFlyweight myAttributes;
|
||||
private final String myText;
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
}
|
||||
|
||||
private void initChunks() {
|
||||
myTextChunks = new ChunkExtractor(getElement(), myRangeMarkers).extractChunks();
|
||||
myTextChunks = ChunkExtractor.extractChunks(getElement(), myRangeMarkers);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
Reference in New Issue
Block a user