@NotNull private static Processor<PsiElement> localProcessor(@NotNull final TextOccurenceProcessor processor, @NotNull final ProgressIndicator progress, final boolean processInjectedPsi, @NotNull final StringSearcher searcher) { return new Processor<PsiElement>() { @Override public boolean process(final PsiElement scopeElement) { return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { @Override public Boolean compute() { return LowLevelSearchUtil.processElementsContainingWordInElement(processor, scopeElement, searcher, processInjectedPsi, progress); } }).booleanValue(); } @Override public String toString() { return processor.toString(); } }; }
public static boolean processTextOccurrences(@NotNull CharSequence text, int startOffset, int endOffset, @NotNull StringSearcher searcher, @Nullable ProgressIndicator progress, @NotNull TIntProcedure processor) { if (endOffset > text.length()) { throw new AssertionError("end>length"); } for (int index = startOffset; index < endOffset; index++) { if (progress != null) progress.checkCanceled(); //noinspection AssignmentToForLoopParameter index = searcher.scan(text, index, endOffset); if (index < 0) break; if (checkJavaIdentifier(text, startOffset, endOffset, searcher, index)) { if (!processor.execute(index)) return false; } } return true; }
@NotNull protected static TreeMap<TextRange, BaseInjection> calcInjections(PsiFile file) { final TreeMap<TextRange, BaseInjection> injectionMap = new TreeMap<TextRange, BaseInjection>(RANGE_COMPARATOR); TIntArrayList ints = new TIntArrayList(); StringSearcher searcher = new StringSearcher("language=", true, true, false); CharSequence contents = file.getViewProvider().getContents(); final char[] contentsArray = CharArrayUtil.fromSequenceWithoutCopying(contents); int s0 = 0, s1 = contents.length(); for (int idx = searcher.scan(contents, contentsArray, s0, s1); idx != -1; idx = searcher.scan(contents, contentsArray, idx + 1, s1)) { ints.add(idx); PsiComment element = PsiTreeUtil.findElementOfClassAtOffset(file, idx, PsiComment.class, false); if (element != null) { String str = ElementManipulators.getValueText(element).trim(); BaseInjection injection = detectInjectionFromText("", str); if (injection != null) { injectionMap.put(element.getTextRange(), injection); } } } return injectionMap; }
private static boolean processElementsWithWordInScopeElement(@NotNull final PsiElement scopeElement, @NotNull final TextOccurenceProcessor processor, @NotNull final String word, final boolean caseSensitive, final boolean ignoreInjectedPsi, final boolean handleEscapeSequences, final ProgressIndicator progress) { return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { @Override public Boolean compute() { StringSearcher searcher = new StringSearcher(word, caseSensitive, true, handleEscapeSequences); return LowLevelSearchUtil.processElementsContainingWordInElement(processor, scopeElement, searcher, !ignoreInjectedPsi, progress); } }).booleanValue(); }
@Nonnull private static Processor<PsiElement> localProcessor(@Nonnull final BulkOccurrenceProcessor processor, @Nonnull final ProgressIndicator progress, @Nonnull final StringSearcher searcher) { return new ReadActionProcessor<PsiElement>() { @Override public boolean processInReadAction(PsiElement scopeElement) { if (scopeElement instanceof PsiCompiledElement) { // can't scan text of the element return true; } return scopeElement.isValid() && processor.execute(scopeElement, LowLevelSearchUtil.getTextOccurrencesInScope(scopeElement, searcher, progress), searcher); } @Override public String toString() { return processor.toString(); } }; }
static boolean processElementsAtOffsets(@Nonnull PsiElement scope, @Nonnull StringSearcher searcher, boolean processInjectedPsi, @Nonnull ProgressIndicator progress, int[] offsetsInScope, @Nonnull TextOccurenceProcessor processor) { if (offsetsInScope.length == 0) return true; Project project = scope.getProject(); TreeElement lastElement = null; for (int offset : offsetsInScope) { progress.checkCanceled(); lastElement = processTreeUp(project, processor, scope, searcher, offset, processInjectedPsi, progress, lastElement); if (lastElement == null) return false; } return true; }
private static Boolean processInjectedFile(PsiElement element, final TextOccurenceProcessor processor, final StringSearcher searcher, ProgressIndicator progress, InjectedLanguageManager injectedLanguageManager) { if (!(element instanceof PsiLanguageInjectionHost)) return null; if (injectedLanguageManager == null) return null; List<Pair<PsiElement,TextRange>> list = injectedLanguageManager.getInjectedPsiFiles(element); if (list == null) return null; for (Pair<PsiElement, TextRange> pair : list) { final PsiElement injected = pair.getFirst(); if (!processElementsContainingWordInElement(processor, injected, searcher, false, progress)) return Boolean.FALSE; } return Boolean.TRUE; }
public static boolean processElementsContainingWordInElement(@NotNull final TextOccurenceProcessor processor, @NotNull final PsiElement scope, @NotNull final StringSearcher searcher, final boolean processInjectedPsi, final ProgressIndicator progress) { if (progress != null) progress.checkCanceled(); PsiFile file = scope.getContainingFile(); FileViewProvider viewProvider = file.getViewProvider(); final CharSequence buffer = viewProvider.getContents(); TextRange range = scope.getTextRange(); if (range == null) { LOG.error("Element " + scope + " of class " + scope.getClass() + " has null range"); return true; } final int scopeStart = range.getStartOffset(); final int startOffset = scopeStart; int endOffset = range.getEndOffset(); if (endOffset > buffer.length()) { diagnoseInvalidRange(scope, file, viewProvider, buffer, range); return true; } final Project project = file.getProject(); final TreeElement[] lastElement = {null}; return processTextOccurrences(buffer, startOffset, endOffset, searcher, progress, new TIntProcedure() { @Override public boolean execute(int offset) { if (progress != null) progress.checkCanceled(); lastElement[0] = processTreeUp(project, processor, scope, searcher, offset - scopeStart, processInjectedPsi, progress, lastElement[0]); return lastElement[0] != null; } }); }
private static int doTest(String pattern, String text) { StringSearcher searcher = new StringSearcher(pattern, true, true, true); final int[] index = {-1}; LowLevelSearchUtil.processTextOccurrences(text, 0, text.length(), searcher, null, new TIntProcedure() { @Override public boolean execute(int value) { index[0] = value; return false; } }); return index[0]; }
@NotNull private FindResult doFindString(@NotNull CharSequence text, @Nullable char[] textArray, int offset, @NotNull FindModel findmodel, @Nullable VirtualFile file) { FindModel model = normalizeIfMultilined(findmodel); String toFind = model.getStringToFind(); if (toFind.isEmpty()){ return NOT_FOUND_RESULT; } if (model.isInCommentsOnly() || model.isInStringLiteralsOnly()) { if (file == null) return NOT_FOUND_RESULT; return findInCommentsAndLiterals(text, textArray, offset, model, file); } if (model.isRegularExpressions()){ return findStringByRegularExpression(text, offset, model); } final StringSearcher searcher = createStringSearcher(model); int index; if (model.isForward()){ final int res = searcher.scan(text, textArray, offset, text.length()); index = res < 0 ? -1 : res; } else { index = offset == 0 ? -1 : searcher.scan(text, textArray, 0, offset-1); } if (index < 0){ return NOT_FOUND_RESULT; } return new FindResultImpl(index, index + toFind.length()); }
public CommentsLiteralsSearchData(VirtualFile lastFile, Set<Language> relevantLanguages, SyntaxHighlighterOverEditorHighlighter highlighter, TokenSet tokensOfInterest, StringSearcher searcher, Matcher matcher, FindModel model) { this.lastFile = lastFile; this.highlighter = highlighter; this.tokensOfInterest = tokensOfInterest; this.searcher = searcher; this.matcher = matcher; this.relevantLanguages = relevantLanguages; this.model = model; }
private static Set<String> calcDevPatternClassNames(@NotNull final Project project) { final List<String> roots = ContainerUtil.createLockFreeCopyOnWriteList(); JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); PsiClass beanClass = psiFacade.findClass(PatternClassBean.class.getName(), GlobalSearchScope.allScope(project)); if (beanClass != null) { GlobalSearchScope scope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(project), StdFileTypes.XML); final TextOccurenceProcessor occurenceProcessor = new TextOccurenceProcessor() { @Override public boolean execute(@NotNull PsiElement element, int offsetInElement) { XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); String className = tag == null ? null : tag.getAttributeValue("className"); if (StringUtil.isNotEmpty(className) && tag.getLocalName().endsWith("patternClass")) { roots.add(className); } return true; } }; final StringSearcher searcher = new StringSearcher("patternClass", true, true); CacheManager.SERVICE.getInstance(beanClass.getProject()).processFilesWithWord(new Processor<PsiFile>() { @Override public boolean process(PsiFile psiFile) { LowLevelSearchUtil.processElementsContainingWordInElement(occurenceProcessor, psiFile, searcher, true, new EmptyProgressIndicator()); return true; } }, searcher.getPattern(), UsageSearchContext.IN_FOREIGN_LANGUAGES, scope, searcher.isCaseSensitive()); } return ContainerUtil.newHashSet(roots); }
@NotNull @Override public AsyncFuture<Boolean> processElementsWithWordAsync(@NotNull final TextOccurenceProcessor processor, @NotNull SearchScope searchScope, @NotNull final String text, final short searchContext, final boolean caseSensitively) { if (text.isEmpty()) { return AsyncFutureFactory.wrapException(new IllegalArgumentException("Cannot search for elements with empty text")); } final ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator(); if (searchScope instanceof GlobalSearchScope) { StringSearcher searcher = new StringSearcher(text, caseSensitively, true, searchContext == UsageSearchContext.IN_STRINGS); return processElementsWithTextInGlobalScopeAsync(processor, (GlobalSearchScope)searchScope, searcher, searchContext, caseSensitively, progress); } else { LocalSearchScope scope = (LocalSearchScope)searchScope; PsiElement[] scopeElements = scope.getScope(); final boolean ignoreInjectedPsi = scope.isIgnoreInjectedPsi(); return JobLauncher.getInstance().invokeConcurrentlyUnderProgressAsync(Arrays.asList(scopeElements), progress, false, new Processor<PsiElement>() { @Override public boolean process(PsiElement scopeElement) { return processElementsWithWordInScopeElement(scopeElement, processor, text, caseSensitively, ignoreInjectedPsi, searchContext == UsageSearchContext.IN_STRINGS, progress); } }); } }
public static boolean processElementsContainingWordInElement(@NotNull TextOccurenceProcessor processor, @NotNull final PsiElement scope, @NotNull StringSearcher searcher, final boolean processInjectedPsi, ProgressIndicator progress) { if (progress != null) progress.checkCanceled(); PsiFile file = scope.getContainingFile(); final CharSequence buffer = file.getViewProvider().getContents(); TextRange range = scope.getTextRange(); if (range == null) { throw new AssertionError("Element " + scope + " of class " + scope.getClass() + " has null range"); } int scopeStart = range.getStartOffset(); int startOffset = scopeStart; int endOffset = range.getEndOffset(); if (endOffset > buffer.length()) { LOG.error("Range for element: '"+scope+"' = "+range+" is out of file '" + file + "' range: " + file.getTextLength()); } final char[] bufferArray = CharArrayUtil.fromSequenceWithoutCopying(buffer); Project project = file.getProject(); do { if (progress != null) progress.checkCanceled(); startOffset = searchWord(buffer, bufferArray, startOffset, endOffset, searcher, progress); if (startOffset < 0) { return true; } if (!processTreeUp(project, processor, scope, searcher, startOffset - scopeStart, processInjectedPsi, progress)) return false; startOffset++; } while (startOffset < endOffset); return true; }
public static int searchWord(@NotNull CharSequence text, int startOffset, int endOffset, @NotNull StringSearcher searcher, @Nullable ProgressIndicator progress) { return searchWord(text, null, startOffset, endOffset, searcher, progress); }
@NotNull private static FindResult doFindString(@NotNull CharSequence text, @Nullable char[] textArray, int offset, @NotNull FindModel findmodel, @Nullable VirtualFile file) { FindModel model = normalizeIfMultilined(findmodel); String toFind = model.getStringToFind(); if (toFind.isEmpty()){ return NOT_FOUND_RESULT; } if (model.isInCommentsOnly() || model.isInStringLiteralsOnly()) { if (file == null) return NOT_FOUND_RESULT; return findInCommentsAndLiterals(text, textArray, offset, model, file); } if (model.isRegularExpressions()){ return findStringByRegularExpression(text, offset, model); } final StringSearcher searcher = createStringSearcher(model); int index; if (model.isForward()){ final int res = searcher.scan(text, textArray, offset, text.length()); index = res < 0 ? -1 : res; } else { index = offset == 0 ? -1 : searcher.scan(text, textArray, 0, offset-1); } if (index < 0){ return NOT_FOUND_RESULT; } return new FindResultImpl(index, index + toFind.length()); }
public CommentsLiteralsSearchData(VirtualFile lastFile, Set<Language> relevantLanguages, SyntaxHighlighter highlighter, TokenSet tokensOfInterest, StringSearcher searcher, Matcher matcher) { this.lastFile = lastFile; this.highlighter = highlighter; this.tokensOfInterest = tokensOfInterest; this.searcher = searcher; this.matcher = matcher; this.relevantLanguages = relevantLanguages; }
private static List<PsiElement> getRootsByClassNames(PsiFile file, String type) { final List<PsiElement> roots = ContainerUtil.createLockFreeCopyOnWriteList(); final Project project = file.getProject(); final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); final PsiClass beanClass = psiFacade.findClass(PatternClassBean.class.getName(), GlobalSearchScope.allScope(project)); if (beanClass != null) { final GlobalSearchScope scope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(project), StdFileTypes.XML); final TextOccurenceProcessor occurenceProcessor = new TextOccurenceProcessor() { @Override public boolean execute(PsiElement element, int offsetInElement) { final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); final String className = tag == null ? null : tag.getAttributeValue("className"); if (className != null && tag.getLocalName().endsWith("patternClass")) { ContainerUtil.addIfNotNull(psiFacade.findClass(className, GlobalSearchScope.allScope(project)), roots); } return true; } }; final StringSearcher searcher = new StringSearcher("patternClass", true, true); CacheManager.SERVICE.getInstance(beanClass.getProject()).processFilesWithWord(new Processor<PsiFile>() { @Override public boolean process(PsiFile psiFile) { LowLevelSearchUtil .processElementsContainingWordInElement(occurenceProcessor, psiFile, searcher, true, new EmptyProgressIndicator()); return true; } }, searcher.getPattern(), UsageSearchContext.IN_FOREIGN_LANGUAGES, scope, searcher.isCaseSensitive()); } final Class[] classes = PatternCompilerFactory.getFactory().getPatternClasses(type); if (classes.length != 0) { roots.add(getRootByClasses(classes, project)); } return roots; }
private static Boolean processInjectedFile(PsiElement element, final TextOccurenceProcessor processor, final StringSearcher searcher, @Nonnull ProgressIndicator progress, InjectedLanguageManager injectedLanguageManager) { if (!(element instanceof PsiLanguageInjectionHost)) return null; if (injectedLanguageManager == null) return null; List<Pair<PsiElement, TextRange>> list = injectedLanguageManager.getInjectedPsiFiles(element); if (list == null) return null; for (Pair<PsiElement, TextRange> pair : list) { final PsiElement injected = pair.getFirst(); if (!processElementsContainingWordInElement(processor, injected, searcher, false, progress)) return Boolean.FALSE; } return Boolean.TRUE; }
@RequiredReadAction public static boolean processElementsContainingWordInElement(@Nonnull final TextOccurenceProcessor processor, @Nonnull final PsiElement scope, @Nonnull final StringSearcher searcher, boolean processInjectedPsi, @Nonnull ProgressIndicator progress) { int[] occurrences = getTextOccurrencesInScope(scope, searcher, progress); return processElementsAtOffsets(scope, searcher, processInjectedPsi, progress, occurrences, processor); }
@RequiredReadAction static int[] getTextOccurrencesInScope(@Nonnull PsiElement scope, @Nonnull StringSearcher searcher, ProgressIndicator progress) { if (progress != null) progress.checkCanceled(); PsiFile file = scope.getContainingFile(); FileViewProvider viewProvider = file.getViewProvider(); final CharSequence buffer = viewProvider.getContents(); TextRange range = scope.getTextRange(); if (range == null) { LOG.error("Element " + scope + " of class " + scope.getClass() + " has null range"); return ArrayUtil.EMPTY_INT_ARRAY; } int startOffset = range.getStartOffset(); int endOffset = range.getEndOffset(); if (endOffset > buffer.length()) { diagnoseInvalidRange(scope, file, viewProvider, buffer, range); return ArrayUtil.EMPTY_INT_ARRAY; } int[] offsets = getTextOccurrences(buffer, startOffset, endOffset, searcher, progress); for (int i = 0; i < offsets.length; i++) { offsets[i] -= startOffset; } return offsets; }
public static boolean processTextOccurrences(@Nonnull CharSequence text, int startOffset, int endOffset, @Nonnull StringSearcher searcher, @javax.annotation.Nullable ProgressIndicator progress, @Nonnull TIntProcedure processor) { for (int offset : getTextOccurrences(text, startOffset, endOffset, searcher, progress)) { if (!processor.execute(offset)) { return false; } } return true; }
@Nonnull private FindResult doFindString(@Nonnull CharSequence text, @Nullable char[] textArray, int offset, @Nonnull FindModel findmodel, @Nullable VirtualFile file) { FindModel model = normalizeIfMultilined(findmodel); String toFind = model.getStringToFind(); if (toFind.isEmpty()) { return NOT_FOUND_RESULT; } if (model.isInCommentsOnly() || model.isInStringLiteralsOnly()) { if (file == null) return NOT_FOUND_RESULT; return findInCommentsAndLiterals(text, textArray, offset, model, file); } if (model.isRegularExpressions()) { return findStringByRegularExpression(text, offset, model); } final StringSearcher searcher = createStringSearcher(model); int index; if (model.isForward()) { final int res = searcher.scan(text, textArray, offset, text.length()); index = res < 0 ? -1 : res; } else { index = offset == 0 ? -1 : searcher.scan(text, textArray, 0, offset - 1); } if (index < 0) { return NOT_FOUND_RESULT; } return new FindResultImpl(index, index + toFind.length()); }
@NotNull private static StringSearcher createStringSearcher(@NotNull FindModel model) { return new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), model.isForward()); }
@NotNull private AsyncFuture<Boolean> processElementsWithTextInGlobalScopeAsync(@NotNull final TextOccurenceProcessor processor, @NotNull final GlobalSearchScope scope, @NotNull final StringSearcher searcher, final short searchContext, final boolean caseSensitively, final ProgressIndicator progress) { if (Thread.holdsLock(PsiLock.LOCK)) { throw new AssertionError("You must not run search from within updating PSI activity. Please consider invokeLatering it instead."); } if (progress != null) { progress.pushState(); progress.setText(PsiBundle.message("psi.scanning.files.progress")); } String text = searcher.getPattern(); List<VirtualFile> fileSet = getFilesWithText(scope, searchContext, caseSensitively, text, progress); if (progress != null) { progress.setText(PsiBundle.message("psi.search.for.word.progress", text)); } final AsyncFuture<Boolean> result = processPsiFileRootsAsync(fileSet, new Processor<PsiElement>() { @Override public boolean process(final PsiElement psiRoot) { return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { public Boolean compute() { return LowLevelSearchUtil.processElementsContainingWordInElement(processor, psiRoot, searcher, true, progress); } }); } }, progress); return new FinallyFuture<Boolean>(result, new Runnable() { @Override public void run() { if (progress != null) { progress.popState(); } } }); }
private static boolean processTreeUp(@NotNull Project project, @NotNull TextOccurenceProcessor processor, @NotNull PsiElement scope, @NotNull StringSearcher searcher, final int offset, final boolean processInjectedPsi, ProgressIndicator progress) { final int scopeStartOffset = scope.getTextRange().getStartOffset(); final int patternLength = searcher.getPatternLength(); ASTNode scopeNode = scope.getNode(); boolean useTree = scopeNode != null; assert scope.isValid(); int start; TreeElement leafNode = null; PsiElement leafElement = null; if (useTree) { leafNode = (LeafElement)scopeNode.findLeafElementAt(offset); if (leafNode == null) return true; start = offset - leafNode.getStartOffset() + scopeStartOffset; } else { if (scope instanceof PsiFile) { leafElement = ((PsiFile)scope).getViewProvider().findElementAt(offset, scope.getLanguage()); } else { leafElement = scope.findElementAt(offset); } if (leafElement == null) return true; assert leafElement.isValid(); start = offset - leafElement.getTextRange().getStartOffset() + scopeStartOffset; } if (start < 0) { LOG.error("offset=" + offset + " scopeStartOffset=" + scopeStartOffset + " leafElement=" + leafElement + " scope=" + scope); } boolean contains = false; PsiElement prev = null; TreeElement prevNode = null; PsiElement run = null; InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(project); while (run != scope) { if (progress != null) progress.checkCanceled(); if (useTree) { start += prevNode == null ? 0 : prevNode.getStartOffsetInParent(); prevNode = leafNode; run = leafNode.getPsi(); } else { start += prev == null ? 0 : prev.getStartOffsetInParent(); prev = run; run = leafElement; } if (!contains) contains = run.getTextLength() - start >= patternLength; //do not compute if already contains if (contains) { if (processInjectedPsi) { Boolean result = processInjectedFile(run, processor, searcher, progress, injectedLanguageManager); if (result != null) { return result.booleanValue(); } } if (!processor.execute(run, start)) { return false; } } if (useTree) { leafNode = leafNode.getTreeParent(); if (leafNode == null) break; } else { leafElement = leafElement.getParent(); if (leafElement == null) break; } } assert run == scope: "Malbuilt PSI: scopeNode="+scope+"; leafNode="+run+"; isAncestor="+ PsiTreeUtil.isAncestor(scope, run, false); return true; }
private static int doTest(String pattern, String text) { StringSearcher searcher = new StringSearcher(pattern, true, true, true); return LowLevelSearchUtil.searchWord(text, 0, text.length(), searcher, null); }
private boolean processElementsWithTextInGlobalScope(@Nonnull final BulkOccurrenceProcessor processor, @Nonnull final GlobalSearchScope scope, @Nonnull final StringSearcher searcher, final short searchContext, final boolean caseSensitively, @javax.annotation.Nullable String containerName, @Nonnull ProgressIndicator progress) { progress.pushState(); boolean result; try { progress.setText(PsiBundle.message("psi.scanning.files.progress")); String text = searcher.getPattern(); Set<VirtualFile> fileSet = new THashSet<>(); getFilesWithText(scope, searchContext, caseSensitively, text, fileSet); progress.setText(PsiBundle.message("psi.search.for.word.progress", text)); final Processor<PsiElement> localProcessor = localProcessor(processor, progress, searcher); if (containerName != null) { List<VirtualFile> intersectionWithContainerFiles = new ArrayList<>(); // intersectionWithContainerFiles holds files containing words from both `text` and `containerName` getFilesWithText(scope, searchContext, caseSensitively, text+" "+containerName, intersectionWithContainerFiles); if (!intersectionWithContainerFiles.isEmpty()) { int totalSize = fileSet.size(); result = processPsiFileRoots(intersectionWithContainerFiles, totalSize, 0, progress, localProcessor); if (result) { fileSet.removeAll(intersectionWithContainerFiles); if (!fileSet.isEmpty()) { result = processPsiFileRoots(new ArrayList<>(fileSet), totalSize, intersectionWithContainerFiles.size(), progress, localProcessor); } } return result; } } result = fileSet.isEmpty() || processPsiFileRoots(new ArrayList<>(fileSet), fileSet.size(), 0, progress, localProcessor); } finally { progress.popState(); } return result; }
@Override public boolean processUsagesInNonJavaFiles(@javax.annotation.Nullable final PsiElement originalElement, @Nonnull String qName, @Nonnull final PsiNonJavaFileReferenceProcessor processor, @Nonnull final GlobalSearchScope initialScope) { if (qName.isEmpty()) { throw new IllegalArgumentException("Cannot search for elements with empty text. Element: "+originalElement+ "; "+(originalElement == null ? null : originalElement.getClass())); } final ProgressIndicator progress = getOrCreateIndicator(); int dotIndex = qName.lastIndexOf('.'); int dollarIndex = qName.lastIndexOf('$'); int maxIndex = Math.max(dotIndex, dollarIndex); final String wordToSearch = maxIndex >= 0 ? qName.substring(maxIndex + 1) : qName; final GlobalSearchScope theSearchScope = ReadAction.compute(() -> { if (originalElement != null && myManager.isInProject(originalElement) && initialScope.isSearchInLibraries()) { return initialScope.intersectWith(GlobalSearchScope.projectScope(myManager.getProject())); } return initialScope; }); PsiFile[] files = myDumbService.runReadActionInSmartMode(() -> CacheManager.getInstance(myManager.getProject()).getFilesWithWord(wordToSearch, UsageSearchContext.IN_PLAIN_TEXT, theSearchScope, true)); final StringSearcher searcher = new StringSearcher(qName, true, true, false); progress.pushState(); final Ref<Boolean> cancelled = Ref.create(Boolean.FALSE); try { progress.setText(PsiBundle.message("psi.search.in.non.java.files.progress")); final SearchScope useScope = originalElement == null ? null : myDumbService.runReadActionInSmartMode(() -> getUseScope(originalElement)); final int patternLength = qName.length(); for (int i = 0; i < files.length; i++) { progress.checkCanceled(); final PsiFile psiFile = files[i]; if (psiFile instanceof PsiBinaryFile) continue; final CharSequence text = ReadAction.compute(() -> psiFile.getViewProvider().getContents()); LowLevelSearchUtil.processTextOccurrences(text, 0, text.length(), searcher, progress, index -> { boolean isReferenceOK = myDumbService.runReadActionInSmartMode(() -> { PsiReference referenceAt = psiFile.findReferenceAt(index); return referenceAt == null || useScope == null || !PsiSearchScopeUtil.isInScope(useScope.intersectWith(initialScope), psiFile); }); if (isReferenceOK && !processor.process(psiFile, index, index + patternLength)) { cancelled.set(Boolean.TRUE); return false; } return true; }); if (cancelled.get()) break; progress.setFraction((double)(i + 1) / files.length); } } finally { progress.popState(); } return !cancelled.get(); }
private static int[] getTextOccurrences(@Nonnull CharSequence text, int startOffset, int endOffset, @Nonnull StringSearcher searcher, @javax.annotation.Nullable ProgressIndicator progress) { if (endOffset > text.length()) { throw new IllegalArgumentException("end: " + endOffset + " > length: " + text.length()); } Map<StringSearcher, int[]> cachedMap = cache.get(text); int[] cachedOccurrences = cachedMap == null ? null : cachedMap.get(searcher); boolean hasCachedOccurrences = cachedOccurrences != null && cachedOccurrences[0] <= startOffset && cachedOccurrences[1] >= endOffset; if (!hasCachedOccurrences) { TIntArrayList occurrences = new TIntArrayList(); int newStart = Math.min(startOffset, cachedOccurrences == null ? startOffset : cachedOccurrences[0]); int newEnd = Math.max(endOffset, cachedOccurrences == null ? endOffset : cachedOccurrences[1]); occurrences.add(newStart); occurrences.add(newEnd); for (int index = newStart; index < newEnd; index++) { if (progress != null) progress.checkCanceled(); //noinspection AssignmentToForLoopParameter index = searcher.scan(text, index, newEnd); if (index < 0) break; if (checkJavaIdentifier(text, 0, text.length(), searcher, index)) { occurrences.add(index); } } cachedOccurrences = occurrences.toNativeArray(); if (cachedMap == null) { cachedMap = ConcurrencyUtil.cacheOrGet(cache, text, ContainerUtil.createConcurrentSoftMap()); } cachedMap.put(searcher, cachedOccurrences); } TIntArrayList offsets = new TIntArrayList(cachedOccurrences.length - 2); for (int i = 2; i < cachedOccurrences.length; i++) { int occurrence = cachedOccurrences[i]; if (occurrence > endOffset - searcher.getPatternLength()) break; if (occurrence >= startOffset) { offsets.add(occurrence); } } return offsets.toNativeArray(); }
@Nonnull private static StringSearcher createStringSearcher(@Nonnull FindModel model) { return new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), model.isForward()); }
@NotNull @Override public DataIndexer<MethodCallData, Void, FileContent> getIndexer() { return inputData -> { if(myOfflineMode) { return Collections.emptyMap(); } int[] nullOffsets = new StringSearcher(PsiKeyword.NULL, true, true).findAllOccurrences(inputData.getContentAsText()); if(nullOffsets.length == 0) { return Collections.emptyMap(); } LighterAST lighterAst = ((FileContentImpl) inputData).getLighterASTForPsiDependentIndex(); Set<LighterASTNode> calls = findCallsWithNulls(lighterAst, nullOffsets); if(calls.isEmpty()) { return Collections.emptyMap(); } Map<MethodCallData, Void> result = new THashMap<>(); for(LighterASTNode element : calls) { final IntArrayList indices = getNullParameterIndices(lighterAst, element); if(indices != null) { final String name = getMethodName(lighterAst, element, element.getTokenType()); if(name != null) { for(int i = 0; i < indices.size(); i++) { result.put(new MethodCallData(name, indices.get(i)), null); } } } } return result; }; }
boolean execute(@Nonnull PsiElement scope, @Nonnull int[] offsetsInScope, @Nonnull StringSearcher searcher);