diff options
Diffstat (limited to 'platform/lang-impl/src/com/intellij/find/impl')
7 files changed, 300 insertions, 160 deletions
diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java index b6a6152b678b..c25456da1988 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -35,7 +35,6 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.*; -import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; @@ -50,8 +49,6 @@ import com.intellij.ui.EditorComboBoxRenderer; import com.intellij.ui.EditorTextField; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.StateRestoringCheckBox; -import com.intellij.ui.components.labels.LinkLabel; -import com.intellij.ui.components.labels.LinkListener; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import com.intellij.util.ui.UIUtil; @@ -60,7 +57,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.text.BadLocationException; import java.awt.*; import java.awt.event.*; import java.util.Arrays; @@ -78,8 +74,7 @@ public class FindDialog extends DialogWrapper { private StateRestoringCheckBox myCbCaseSensitive; private StateRestoringCheckBox myCbPreserveCase; private StateRestoringCheckBox myCbWholeWordsOnly; - private StateRestoringCheckBox myCbInCommentsOnly; - private StateRestoringCheckBox myCbInStringLiteralsOnly; + private ComboBox mySearchContext; private StateRestoringCheckBox myCbRegularExpressions; private JRadioButton myRbGlobal; private JRadioButton myRbSelectedText; @@ -223,7 +218,7 @@ public class FindDialog extends DialogWrapper { myReplacePrompt.setVisible(myModel.isReplaceState()); myReplaceComboBox.setVisible(myModel.isReplaceState()); if (myCbToSkipResultsWhenOneUsage != null) { - myCbToSkipResultsWhenOneUsage.setVisible(myModel.isReplaceState()); + myCbToSkipResultsWhenOneUsage.setVisible(!myModel.isReplaceState()); } myCbPreserveCase.setVisible(myModel.isReplaceState()); } @@ -354,9 +349,9 @@ public class FindDialog extends DialogWrapper { gbConstraints.gridwidth = GridBagConstraints.REMAINDER; optionsPanel.add(createFilterPanel(),gbConstraints); - myCbToSkipResultsWhenOneUsage = createCheckbox(FindSettings.getInstance().isSkipResultsWithOneUsage(), FindBundle.message("find.options.skip.results.tab.with.one.usage.checkbox")); + myCbToSkipResultsWhenOneUsage = createCheckbox(FindSettings.getInstance().isSkipResultsWithOneUsage(), FindBundle.message("find.options.skip.results.tab.with.one.occurrence.checkbox")); optionsPanel.add(myCbToSkipResultsWhenOneUsage, gbConstraints); - myCbToSkipResultsWhenOneUsage.setVisible(myModel.isReplaceState()); + myCbToSkipResultsWhenOneUsage.setVisible(!myModel.isReplaceState()); } else { if (FindManagerImpl.ourHasSearchInCommentsAndLiterals) { @@ -476,6 +471,9 @@ public class FindDialog extends DialogWrapper { findSettings.setWholeWordsOnly(myModel.isWholeWordsOnly()); findSettings.setInStringLiteralsOnly(myModel.isInStringLiteralsOnly()); findSettings.setInCommentsOnly(myModel.isInCommentsOnly()); + findSettings.setExceptComments(myModel.isExceptComments()); + findSettings.setExceptStringLiterals(myModel.isExceptStringLiterals()); + findSettings.setExceptCommentsAndLiterals(myModel.isExceptCommentsAndStringLiterals()); findSettings.setRegularExpressions(myModel.isRegularExpressions()); if (!myModel.isMultipleFiles()){ @@ -603,39 +601,26 @@ public class FindDialog extends DialogWrapper { regExPanel.setLayout(new BoxLayout(regExPanel, BoxLayout.X_AXIS)); regExPanel.add(myCbRegularExpressions); - regExPanel.add(new LinkLabel("[Help]", null, new LinkListener() { - @Override - public void linkSelected(LinkLabel aSource, Object aLinkData) { - try { - final JBPopup helpPopup = RegExHelpPopup.createRegExHelpPopup(); - helpPopup.showInCenterOf(regExPanel); - } - catch (BadLocationException e) { - LOG.info(e); - } - } - })); + regExPanel.add(RegExHelpPopup.createRegExLink("[Help]", regExPanel, LOG)); findOptionsPanel.add(regExPanel); - myCbInCommentsOnly = createCheckbox(FindBundle.message("find.options.comments.only")); - myCbInStringLiteralsOnly = createCheckbox(FindBundle.message("find.options.string.literals.only")); - ItemListener itemListener = new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getSource() == myCbInCommentsOnly) { - if (myCbInCommentsOnly.isSelected()) myCbInStringLiteralsOnly.setSelected(false); - } else if (e.getSource() == myCbInStringLiteralsOnly) { - if (myCbInStringLiteralsOnly.isSelected()) myCbInCommentsOnly.setSelected(false); - } - } - }; - myCbInCommentsOnly.addItemListener(itemListener); - myCbInStringLiteralsOnly.addItemListener(itemListener); + mySearchContext = new ComboBox(new Object[] {FindBundle.message("find.context.anywhere.scope.label", 200), + FindBundle.message("find.context.in.comments.scope.label"), FindBundle.message("find.context.in.literals.scope.label"), + FindBundle.message("find.context.except.comments.scope.label"), + FindBundle.message("find.context.except.literals.scope.label"), + FindBundle.message("find.context.except.comments.and.literals.scope.label")}); + final JPanel searchContextPanel = new JPanel(new BorderLayout()); + searchContextPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + + JLabel searchContextLabel = new JLabel(FindBundle.message("find.context.combo.label")); + searchContextLabel.setLabelFor(mySearchContext); + searchContextPanel.add(searchContextLabel, BorderLayout.WEST); + + searchContextPanel.add(mySearchContext, BorderLayout.CENTER); if (FindManagerImpl.ourHasSearchInCommentsAndLiterals) { - findOptionsPanel.add(myCbInCommentsOnly); - findOptionsPanel.add(myCbInStringLiteralsOnly); + findOptionsPanel.add(searchContextPanel); } ActionListener actionListener = new ActionListener() { @@ -1000,9 +985,25 @@ public class FindDialog extends DialogWrapper { } model.setWholeWordsOnly(myCbWholeWordsOnly.isSelected()); - model.setInStringLiteralsOnly(myCbInStringLiteralsOnly.isSelected()); - model.setInCommentsOnly(myCbInCommentsOnly.isSelected()); + String selectedSearchContextInUi = (String)mySearchContext.getSelectedItem(); + FindModel.SearchContext searchContext = FindModel.SearchContext.ANY; + if (FindBundle.message("find.context.in.literals.scope.label").equals(selectedSearchContextInUi)) { + searchContext = FindModel.SearchContext.IN_STRING_LITERALS; + } + else if (FindBundle.message("find.context.in.comments.scope.label").equals(selectedSearchContextInUi)) { + searchContext = FindModel.SearchContext.IN_COMMENTS; + } + else if (FindBundle.message("find.context.except.comments.scope.label").equals(selectedSearchContextInUi)) { + searchContext = FindModel.SearchContext.EXCEPT_COMMENTS; + } + else if (FindBundle.message("find.context.except.literals.scope.label").equals(selectedSearchContextInUi)) { + searchContext = FindModel.SearchContext.EXCEPT_STRING_LITERALS; + } else if (FindBundle.message("find.context.except.comments.and.literals.scope.label").equals(selectedSearchContextInUi)) { + searchContext = FindModel.SearchContext.EXCEPT_COMMENTS_AND_STRING_LITERALS; + } + + model.setSearchContext(searchContext); model.setRegularExpressions(myCbRegularExpressions.isSelected()); String stringToFind = getStringToFind(); @@ -1068,8 +1069,14 @@ public class FindDialog extends DialogWrapper { private void initByModel() { myCbCaseSensitive.setSelected(myModel.isCaseSensitive()); myCbWholeWordsOnly.setSelected(myModel.isWholeWordsOnly()); - myCbInStringLiteralsOnly.setSelected(myModel.isInStringLiteralsOnly()); - myCbInCommentsOnly.setSelected(myModel.isInCommentsOnly()); + String searchContext = FindBundle.message("find.context.anywhere.scope.label"); + if (myModel.isInCommentsOnly()) searchContext = FindBundle.message("find.context.in.comments.scope.label"); + else if (myModel.isInStringLiteralsOnly()) searchContext = FindBundle.message("find.context.in.literals.scope.label"); + else if (myModel.isExceptStringLiterals()) searchContext = FindBundle.message("find.context.except.literals.scope.label"); + else if (myModel.isExceptComments()) searchContext = FindBundle.message("find.context.except.comments.scope.label"); + else if (myModel.isExceptCommentsAndStringLiterals()) searchContext = FindBundle.message("find.context.except.comments.and.literals.scope.label"); + mySearchContext.setSelectedItem(searchContext); + myCbRegularExpressions.setSelected(myModel.isRegularExpressions()); if (myModel.isMultipleFiles()) { diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java index 59adf1e62bd0..7e9e71d7a94d 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java @@ -19,6 +19,7 @@ import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; import com.intellij.find.FindBundle; import com.intellij.find.FindModel; +import com.intellij.find.findInProject.FindInProjectManager; import com.intellij.find.ngrams.TrigramIndex; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; @@ -182,7 +183,7 @@ class FindInProjectTask { private void searchInFiles(@NotNull Collection<PsiFile> psiFiles, @NotNull FindUsagesProcessPresentation processPresentation, - @NotNull Processor<UsageInfo> consumer) { + @NotNull final Processor<UsageInfo> consumer) { int i = 0; long totalFilesSize = 0; int count = 0; @@ -195,7 +196,8 @@ class FindInProjectTask { long fileLength = UsageViewManagerImpl.getFileLength(virtualFile); if (fileLength == -1) continue; // Binary or invalid - if (ProjectCoreUtil.isProjectOrWorkspaceFile(virtualFile) && !Registry.is("find.search.in.project.files")) continue; + final boolean skipProjectFile = ProjectCoreUtil.isProjectOrWorkspaceFile(virtualFile) && !myFindModel.isSearchInProjectFiles(); + if (skipProjectFile && !Registry.is("find.search.in.project.files")) continue; if (fileLength > SINGLE_FILE_SIZE_LIMIT) { myLargeFiles.add(psiFile); @@ -209,7 +211,24 @@ class FindInProjectTask { myProgress.setText(text); myProgress.setText2(FindBundle.message("find.searching.for.string.in.file.occurrences.progress", count)); - int countInFile = FindInProjectUtil.processUsagesInFile(psiFile, myFindModel, consumer); + int countInFile = FindInProjectUtil.processUsagesInFile(psiFile, myFindModel, new Processor<UsageInfo>() { + @Override + public boolean process(UsageInfo info) { + return skipProjectFile || consumer.process(info); + } + }); + + if (countInFile > 0 && skipProjectFile) { + processPresentation.projectFileUsagesFound(new Runnable() { + @Override + public void run() { + FindModel model = myFindModel.clone(); + model.setSearchInProjectFiles(true); + FindInProjectManager.getInstance(myProject).startFindInProject(model); + } + }); + continue; + } count += countInFile; if (countInFile > 0) { @@ -231,6 +250,7 @@ class FindInProjectTask { final GlobalSearchScope globalCustomScope = toGlobal(customScope); final ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(myProject); + final boolean hasTrigrams = hasTrigrams(myFindModel.getStringToFind()); class EnumContentIterator implements ContentIterator { final Set<PsiFile> myFiles = new LinkedHashSet<PsiFile>(); @@ -238,8 +258,6 @@ class FindInProjectTask { @Override public boolean processFile(@NotNull final VirtualFile virtualFile) { ApplicationManager.getApplication().runReadAction(new Runnable() { - final boolean hasTrigrams = hasTrigrams(myFindModel.getStringToFind()); - @Override public void run() { ProgressManager.checkCanceled(); @@ -249,9 +267,6 @@ class FindInProjectTask { return; } - if (virtualFile.getFileType().isBinary()) { - return; - } if (skipIndexed && isCoveredByIndex(virtualFile) && (fileIndex.isInContent(virtualFile) || fileIndex.isInLibraryClasses(virtualFile) || fileIndex.isInLibrarySource(virtualFile))) { return; @@ -261,7 +276,9 @@ class FindInProjectTask { if (psiFile != null && !(psiFile instanceof PsiBinaryFile) && !alreadySearched.contains(psiFile)) { PsiFile sourceFile = (PsiFile)psiFile.getNavigationElement(); if (sourceFile != null) psiFile = sourceFile; - myFiles.add(psiFile); + if (!psiFile.getFileType().isBinary()) { + myFiles.add(psiFile); + } } } @@ -290,7 +307,9 @@ class FindInProjectTask { for (VirtualFile file : getLocalScopeFiles((LocalSearchScope)customScope)) { iterator.processFile(file); } - } else if (customScope instanceof Iterable) { // GlobalSearchScope can span files out of project roots e.g. FileScope / FilesScope + } + else if (customScope instanceof Iterable) { // GlobalSearchScope can span files out of project roots e.g. FileScope / FilesScope + //noinspection unchecked for (VirtualFile file : (Iterable<VirtualFile>)customScope) { iterator.processFile(file); } @@ -382,16 +401,14 @@ class FindInProjectTask { return myFindModel.isWholeWordsOnly() && text.indexOf('$') < 0 && !StringUtil.getWordsInStringLongestFirst(text).isEmpty(); } - private static boolean hasTrigrams(String text) { - if (TrigramIndex.ENABLED) { - return !TrigramBuilder.processTrigrams(text, new TrigramBuilder.TrigramProcessor() { - @Override - public boolean execute(int value) { - return false; - } - }); - } - return false; + private static boolean hasTrigrams(@NotNull String text) { + return TrigramIndex.ENABLED && + !TrigramBuilder.processTrigrams(text, new TrigramBuilder.TrigramProcessor() { + @Override + public boolean execute(int value) { + return false; + } + }); } @@ -430,6 +447,7 @@ class FindInProjectTask { final List<VirtualFile> hits = new ArrayList<VirtualFile>(); final GlobalSearchScope finalScope = scope; ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override public void run() { FileBasedIndex.getInstance().getFilesWithKey(TrigramIndex.INDEX_ID, keys, new CommonProcessors.CollectProcessor<VirtualFile>(hits), finalScope); diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java index e4570b7379bf..23f14c5eb0b4 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java @@ -47,7 +47,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.ex.VirtualFileManagerEx; import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.ui.content.Content; @@ -55,7 +54,6 @@ import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewManager; import com.intellij.usages.ConfigurableUsageTarget; import com.intellij.usages.FindUsagesProcessPresentation; -import com.intellij.usages.UsageView; import com.intellij.usages.UsageViewPresentation; import com.intellij.util.Function; import com.intellij.util.PatternUtil; @@ -326,7 +324,7 @@ public class FindInProjectUtil { return processPresentation; } - public static class StringUsageTarget implements ConfigurableUsageTarget, ItemPresentation, TypeSafeDataProvider { + public static class StringUsageTarget implements ConfigurableUsageTarget, ItemPresentation { @NotNull protected final Project myProject; @NotNull protected final FindModel myFindModel; @@ -425,12 +423,5 @@ public class FindInProjectUtil { public KeyboardShortcut getShortcut() { return ActionManager.getInstance().getKeyboardShortcut("FindInPath"); } - - @Override - public void calcData(DataKey key, DataSink sink) { - if (key == UsageView.USAGE_SCOPE) { - sink.put(UsageView.USAGE_SCOPE, GlobalSearchScope.allScope(myProject)); - } - } } } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java index e327ec79c227..27e2b7215f25 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java @@ -27,7 +27,6 @@ import com.intellij.find.impl.livePreview.SearchResults; import com.intellij.lang.Language; import com.intellij.lang.LanguageParserDefinitions; import com.intellij.lang.ParserDefinition; -import com.intellij.lexer.LayeredLexer; import com.intellij.lexer.Lexer; import com.intellij.navigation.NavigationItem; import com.intellij.openapi.actionSystem.ActionManager; @@ -42,11 +41,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.ex.FoldingModelEx; -import com.intellij.openapi.editor.ex.util.LayeredHighlighterIterator; -import com.intellij.openapi.editor.ex.util.LayeredLexerEditorHighlighter; -import com.intellij.openapi.editor.highlighter.EditorHighlighter; -import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; -import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.TextEditor; @@ -58,15 +52,17 @@ import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.intellij.psi.impl.search.LexerEditorHighlighterLexer; import com.intellij.psi.search.SearchScope; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.ui.LightweightHint; import com.intellij.ui.ReplacePromptDialog; +import com.intellij.usages.ChunkExtractor; import com.intellij.usages.UsageViewManager; +import com.intellij.usages.impl.SyntaxHighlighterOverEditorHighlighter; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.Predicate; import com.intellij.util.messages.MessageBus; import com.intellij.util.text.CharArrayUtil; import com.intellij.util.text.StringSearcher; @@ -96,6 +92,7 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo private final FindUsagesManager myFindUsagesManager; private boolean isFindWasPerformed = false; + private boolean isSelectNextOccurrenceWasPerformed = false; private Point myReplaceInFilePromptPos = new Point(-1, -1); private Point myReplaceInProjectPromptPos = new Point(-1, -1); private final FindModel myFindInProjectModel = new FindModel(); @@ -257,7 +254,18 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo @Override public void setFindWasPerformed() { isFindWasPerformed = true; - //myFindUsagesManager.clearFindingNextUsageInFile(); + isSelectNextOccurrenceWasPerformed = false; + } + + @Override + public boolean selectNextOccurrenceWasPerformed() { + return isSelectNextOccurrenceWasPerformed; + } + + @Override + public void setSelectNextOccurrenceWasPerformed() { + isSelectNextOccurrenceWasPerformed = true; + isFindWasPerformed = false; } @Override @@ -270,7 +278,7 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo if (myFindNextModel == null) return null; final JComponent header = editor.getHeaderComponent(); - if (header instanceof EditorSearchComponent) { + if (header instanceof EditorSearchComponent && !isSelectNextOccurrenceWasPerformed) { final EditorSearchComponent searchComponent = (EditorSearchComponent)header; final String textInField = searchComponent.getTextInField(); if (!Comparing.equal(textInField, myFindInFileModel.getStringToFind()) && !textInField.isEmpty()) { @@ -305,25 +313,103 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo LOG.debug(model.toString()); } - final char[] textArray = CharArrayUtil.fromSequenceWithoutCopying(text); + return findStringLoop(text, offset, model, file, getFindContextPredicate(model, file, text)); + } + private FindResult findStringLoop(CharSequence text, int offset, FindModel model, VirtualFile file, @Nullable Predicate<FindResult> filter) { + final char[] textArray = CharArrayUtil.fromSequenceWithoutCopying(text); while(true) { FindResult result = doFindString(text, textArray, offset, model, file); + if (filter == null || filter.apply(result)) { + if (!model.isWholeWordsOnly()) { + return result; + } + if (!result.isStringFound()) { + return result; + } + if (isWholeWord(text, result.getStartOffset(), result.getEndOffset())) { + return result; + } + } + + offset = model.isForward() ? result.getStartOffset() + 1 : result.getEndOffset() - 1; + if (offset > text.length() || offset < 0) return NOT_FOUND_RESULT; + } + } + + private class FindExceptCommentsOrLiteralsData implements Predicate<FindResult> { + private final VirtualFile myFile; + private final FindModel myFindModel; + private final TreeMap<Integer, Integer> mySkipRangesSet; + + private FindExceptCommentsOrLiteralsData(VirtualFile file, FindModel model, CharSequence text) { + myFile = file; + myFindModel = model.clone(); - if (!model.isWholeWordsOnly()) { - return result; + TreeMap<Integer, Integer> result = new TreeMap<Integer, Integer>(); + + if (model.isExceptComments() || model.isExceptCommentsAndStringLiterals()) { + addRanges(file, model, text, result, FindModel.SearchContext.IN_COMMENTS); } - if (!result.isStringFound()){ - return result; + + if (model.isExceptStringLiterals() || model.isExceptCommentsAndStringLiterals()) { + addRanges(file, model, text, result, FindModel.SearchContext.IN_STRING_LITERALS); } - if (isWholeWord(text, result.getStartOffset(), result.getEndOffset())){ - return result; + + mySkipRangesSet = result; + } + + private void addRanges(VirtualFile file, + FindModel model, + CharSequence text, + TreeMap<Integer, Integer> result, + FindModel.SearchContext searchContext) { + FindModel clonedModel = model.clone(); + clonedModel.setSearchContext(searchContext); + clonedModel.setForward(true); + int offset = 0; + + while(true) { + FindResult customResult = findStringLoop(text, offset, clonedModel, file, null); + if (!customResult.isStringFound()) break; + result.put(customResult.getStartOffset(), customResult.getEndOffset()); + offset = Math.max(customResult.getEndOffset(), offset + 1); // avoid loop for zero size reg exps matches + if (offset >= text.length()) break; } + } - offset = model.isForward() ? result.getStartOffset() + 1 : result.getEndOffset() - 1; - if (offset > text.length() || offset < 0) return NOT_FOUND_RESULT; + boolean isAcceptableFor(FindModel model, VirtualFile file) { + return Comparing.equal(myFile, file) && myFindModel.equals(model); + } + + @Override + public boolean apply(@Nullable FindResult input) { + if (input == null || !input.isStringFound()) return true; + NavigableMap<Integer, Integer> map = mySkipRangesSet.headMap(input.getStartOffset(), true); + for(Map.Entry<Integer, Integer> e:map.descendingMap().entrySet()) { + if (e.getKey() <= input.getStartOffset() && e.getValue() >= input.getEndOffset()) return false; + if (e.getValue() <= input.getStartOffset()) break; + } + return true; } } + private static Key<FindExceptCommentsOrLiteralsData> ourExceptCommentsOrLiteralsDataKey = Key.create("except.comments.literals.search.data"); + + private Predicate<FindResult> getFindContextPredicate(@NotNull FindModel model, VirtualFile file, CharSequence text) { + if (file == null) return null; + FindModel.SearchContext context = model.getSearchContext(); + if( context == FindModel.SearchContext.ANY || context == FindModel.SearchContext.IN_COMMENTS || + context == FindModel.SearchContext.IN_STRING_LITERALS) { + return null; + } + + FindExceptCommentsOrLiteralsData data = model.getUserData(ourExceptCommentsOrLiteralsDataKey); + if (data == null || !data.isAcceptableFor(model, file)) { + model.putUserData(ourExceptCommentsOrLiteralsDataKey, data = new FindExceptCommentsOrLiteralsData(file, model, text)); + } + + return data; + } @Override public int showMalformedReplacementPrompt(@NotNull FindModel model, String title, MalformedReplacementStringException exception) { @@ -408,11 +494,15 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo return new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), model.isForward()); } + public static void clearPreviousFindData(FindModel model) { + model.putUserData(ourCommentsLiteralsSearchDataKey, null); + model.putUserData(ourExceptCommentsOrLiteralsDataKey, null); + } + private static class CommentsLiteralsSearchData { final VirtualFile lastFile; int startOffset = 0; - final SyntaxHighlighter highlighter; - final Lexer highlightingLexer; + final SyntaxHighlighterOverEditorHighlighter highlighter; TokenSet tokensOfInterest; final StringSearcher searcher; @@ -420,8 +510,8 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo final Set<Language> relevantLanguages; final FindModel model; - public CommentsLiteralsSearchData(VirtualFile lastFile, Set<Language> relevantLanguages, SyntaxHighlighter highlighter, - Lexer lexer, TokenSet tokensOfInterest, + public CommentsLiteralsSearchData(VirtualFile lastFile, Set<Language> relevantLanguages, + SyntaxHighlighterOverEditorHighlighter highlighter, TokenSet tokensOfInterest, StringSearcher searcher, Matcher matcher, FindModel model) { this.lastFile = lastFile; this.highlighter = highlighter; @@ -429,12 +519,11 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo this.searcher = searcher; this.matcher = matcher; this.relevantLanguages = relevantLanguages; - highlightingLexer = lexer; this.model = model; } } - public static final Key<CommentsLiteralsSearchData> ourCommentsLiteralsSearchDataKey = Key.create("comments.literals.search.data"); + private static final Key<CommentsLiteralsSearchData> ourCommentsLiteralsSearchDataKey = Key.create("comments.literals.search.data"); @NotNull private FindResult findInCommentsAndLiterals(@NotNull CharSequence text, @@ -512,38 +601,15 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo Matcher matcher = model.isRegularExpressions() ? compileRegExp(model, ""):null; StringSearcher searcher = matcher != null ? null: new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), true); - LayeredLexer.ourDisableLayersFlag.set(Boolean.TRUE); - EditorHighlighter editorHighlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, file); - Lexer lexer; - - try { - if (editorHighlighter instanceof LayeredLexerEditorHighlighter) { - lexer = new LexerEditorHighlighterLexer(editorHighlighter, false); - } else { - lexer = highlighter.getHighlightingLexer(); - } - } - finally { - LayeredLexer.ourDisableLayersFlag.set(null); - } - - data = new CommentsLiteralsSearchData(file, relevantLanguages, highlighter, lexer, tokensOfInterest, searcher, matcher, (FindModel)model.clone()); - lexer.start(text, 0, text.length(), 0); + SyntaxHighlighterOverEditorHighlighter highlighterAdapter = new SyntaxHighlighterOverEditorHighlighter(highlighter, file, myProject); + data = new CommentsLiteralsSearchData(file, relevantLanguages, highlighterAdapter, tokensOfInterest, searcher, matcher, model.clone()); + data.highlighter.restart(text); model.putUserData(ourCommentsLiteralsSearchDataKey, data); } int initialStartOffset = model.isForward() && data.startOffset < offset ? data.startOffset : 0; - final Lexer lexer = data.highlightingLexer; - LayeredHighlighterIterator layeredHighlighterIterator = null; - if (lexer instanceof LexerEditorHighlighterLexer) { - ((LexerEditorHighlighterLexer)lexer).resetPosition(initialStartOffset); - HighlighterIterator iterator = ((LexerEditorHighlighterLexer)lexer).getHighlighterIterator(); - if (iterator instanceof LayeredHighlighterIterator) { - layeredHighlighterIterator = (LayeredHighlighterIterator)iterator; - } - } else { - lexer.start(text, initialStartOffset, text.length(), 0); - } + data.highlighter.resetPosition(initialStartOffset); + final Lexer lexer = data.highlighter.getHighlightingLexer(); IElementType tokenType; TokenSet tokens = data.tokensOfInterest; @@ -555,13 +621,11 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo while((tokenType = lexer.getTokenType()) != null) { if (lexer.getState() == 0) lastGoodOffset = lexer.getTokenStart(); - final SyntaxHighlighter activeSyntaxHighlighter = - layeredHighlighterIterator != null ? layeredHighlighterIterator.getActiveSyntaxHighlighter() : data.highlighter; - final TextAttributesKey[] keys = activeSyntaxHighlighter.getTokenHighlights(tokenType); + final TextAttributesKey[] keys = data.highlighter.getTokenHighlights(tokenType); if (tokens.contains(tokenType) || - (model.isInStringLiteralsOnly() && isHighlightedAsString(keys)) || - (model.isInCommentsOnly() && isHighlightedAsDocComment(keys)) + (model.isInStringLiteralsOnly() && ChunkExtractor.isHighlightedAsString(keys)) || + (model.isInCommentsOnly() && ChunkExtractor.isHighlightedAsComment(keys)) ) { int start = lexer.getTokenStart(); int end = lexer.getTokenEnd(); @@ -634,32 +698,6 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo return prevFindResult; } - private static boolean isHighlightedAsDocComment(TextAttributesKey... keys) { - for (TextAttributesKey key : keys) { - if (key == DefaultLanguageHighlighterColors.DOC_COMMENT || key == SyntaxHighlighterColors.DOC_COMMENT) { - return true; - } - final TextAttributesKey fallbackAttributeKey = key.getFallbackAttributeKey(); - if (fallbackAttributeKey != null && isHighlightedAsDocComment(fallbackAttributeKey)) { - return true; - } - } - return false; - } - - private static boolean isHighlightedAsString(TextAttributesKey... keys) { - for (TextAttributesKey key : keys) { - if (key == DefaultLanguageHighlighterColors.STRING || key == SyntaxHighlighterColors.STRING) { - return true; - } - final TextAttributesKey fallbackAttributeKey = key.getFallbackAttributeKey(); - if (fallbackAttributeKey != null && isHighlightedAsString(fallbackAttributeKey)) { - return true; - } - } - return false; - } - private static TokenSet addTokenTypesForLanguage(FindModel model, Language lang, TokenSet tokensOfInterest) { ParserDefinition definition = LanguageParserDefinitions.INSTANCE.forLanguage(lang); if (definition != null) { diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindResultUsageInfo.java b/platform/lang-impl/src/com/intellij/find/impl/FindResultUsageInfo.java index 2c372795ea85..ee8d98422250 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindResultUsageInfo.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindResultUsageInfo.java @@ -80,7 +80,7 @@ public class FindResultUsageInfo extends UsageInfo { Long data = myFindModel.getUserData(ourDocumentTimestampKey); if (data == null || data != myTimestamp) { data = myTimestamp; - myFindModel.putUserData(FindManagerImpl.ourCommentsLiteralsSearchDataKey, null); + FindManagerImpl.clearPreviousFindData(myFindModel); } myFindModel.putUserData(ourDocumentTimestampKey, data); FindResult result; @@ -110,7 +110,13 @@ public class FindResultUsageInfo extends UsageInfo { assert result.isStringFound(); - if (myFindModel.isRegularExpressions() || myFindModel.isInCommentsOnly() || myFindModel.isInStringLiteralsOnly()) { + if (myFindModel.isRegularExpressions() || + myFindModel.isInCommentsOnly() || + myFindModel.isInStringLiteralsOnly() || + myFindModel.isExceptStringLiterals() || + myFindModel.isExceptCommentsAndStringLiterals() || + myFindModel.isExceptComments() + ) { myAnchor = SmartPointerManager.getInstance(getProject()).createSmartPsiFileRangePointer(file, TextRange.from(offset, 0)); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindSettingsImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindSettingsImpl.java index 5fb74f190ac0..c9c8e3492834 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindSettingsImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindSettingsImpl.java @@ -103,6 +103,9 @@ public class FindSettingsImpl extends FindSettings implements PersistentStateCom @SuppressWarnings({"WeakerAccess"}) public boolean WHOLE_WORDS_ONLY = false; @SuppressWarnings({"WeakerAccess"}) public boolean COMMENTS_ONLY = false; @SuppressWarnings({"WeakerAccess"}) public boolean STRING_LITERALS_ONLY = false; + @SuppressWarnings({"WeakerAccess"}) public boolean EXCEPT_COMMENTS = false; + @SuppressWarnings({"WeakerAccess"}) public boolean EXCEPT_COMMENTS_AND_STRING_LITERALS = false; + @SuppressWarnings({"WeakerAccess"}) public boolean EXCEPT_STRING_LITERALS = false; @SuppressWarnings({"WeakerAccess"}) public boolean LOCAL_WHOLE_WORDS_ONLY = false; @SuppressWarnings({"WeakerAccess"}) public boolean REGULAR_EXPRESSIONS = false; @SuppressWarnings({"WeakerAccess"}) public boolean LOCAL_REGULAR_EXPRESSIONS = false; @@ -276,8 +279,18 @@ public class FindSettingsImpl extends FindSettings implements PersistentStateCom model.setGlobal(isGlobal()); model.setRegularExpressions(isRegularExpressions()); model.setWholeWordsOnly(isWholeWordsOnly()); - model.setInCommentsOnly(isInCommentsOnly()); - model.setInStringLiteralsOnly(isInStringLiteralsOnly()); + FindModel.SearchContext searchContext = isInCommentsOnly() ? + FindModel.SearchContext.IN_COMMENTS : + isInStringLiteralsOnly() ? + FindModel.SearchContext.IN_STRING_LITERALS : + isExceptComments() ? + FindModel.SearchContext.EXCEPT_COMMENTS : + isExceptStringLiterals() ? + FindModel.SearchContext.EXCEPT_STRING_LITERALS : + isExceptCommentsAndLiterals() ? + FindModel.SearchContext.EXCEPT_COMMENTS_AND_STRING_LITERALS : + FindModel.SearchContext.ANY; + model.setSearchContext(searchContext); model.setWithSubdirectories(isWithSubdirectories()); model.setFileFilter(FILE_MASK); @@ -384,4 +397,34 @@ public class FindSettingsImpl extends FindSettings implements PersistentStateCom public void setCustomScope(final String SEARCH_SCOPE) { this.SEARCH_SCOPE = SEARCH_SCOPE; } + + @Override + public boolean isExceptComments() { + return EXCEPT_COMMENTS; + } + + @Override + public void setExceptCommentsAndLiterals(boolean selected) { + EXCEPT_COMMENTS_AND_STRING_LITERALS = selected; + } + + @Override + public boolean isExceptCommentsAndLiterals() { + return EXCEPT_COMMENTS_AND_STRING_LITERALS; + } + + @Override + public void setExceptComments(boolean selected) { + EXCEPT_COMMENTS = selected; + } + + @Override + public boolean isExceptStringLiterals() { + return EXCEPT_STRING_LITERALS; + } + + @Override + public void setExceptStringLiterals(boolean selected) { + EXCEPT_STRING_LITERALS = selected; + } } diff --git a/platform/lang-impl/src/com/intellij/find/impl/RegExHelpPopup.java b/platform/lang-impl/src/com/intellij/find/impl/RegExHelpPopup.java index 23dee12e362f..0aace901a409 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/RegExHelpPopup.java +++ b/platform/lang-impl/src/com/intellij/find/impl/RegExHelpPopup.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -17,12 +17,19 @@ package com.intellij.find.impl; import com.intellij.codeInsight.hint.HintUtil; import com.intellij.ide.BrowserUtil; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.ui.popup.ComponentPopupBuilder; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.util.MinimizeButton; +import com.intellij.openapi.util.Disposer; import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.components.labels.LinkLabel; +import com.intellij.ui.components.labels.LinkListener; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.HyperlinkEvent; @@ -331,6 +338,36 @@ public class RegExHelpPopup extends JPanel { add(myScrollPane, BorderLayout.CENTER); } + @NotNull + public static LinkLabel createRegExLink(@NotNull String title, @Nullable final Component owner, @Nullable final Logger logger) { + return new LinkLabel(title, null, new LinkListener() { + JBPopup helpPopup; + @Override + public void linkSelected(LinkLabel aSource, Object aLinkData) { + try { + if (helpPopup != null && !helpPopup.isDisposed() && helpPopup.isVisible()) { + return; + } + helpPopup = createRegExHelpPopup(); + Disposer.register(helpPopup, new Disposable() { + @Override + public void dispose() { + destroyPopup(); + } + }); + helpPopup.showInCenterOf(owner); + } + catch (BadLocationException e) { + if (logger != null) logger.info(e); + } + } + + private void destroyPopup() { + helpPopup = null; + } + }); + } + @Override public Dimension getPreferredSize() { return new Dimension(600, 300); |