From d795ce83684572b516cea568403a3f4a82adca95 Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Tue, 26 Sep 2017 19:00:49 +0300 Subject: [PATCH] IDEA-177846 Improve Replace in Path --- .../intellij/find/impl/FindPopupPanel.java | 233 +++++++++++++----- .../ReplaceInProjectManager.java | 27 +- .../src/messages/FindBundle.properties | 13 +- .../src/messages/UIBundle.properties | 1 + .../src/messages/UsageView.properties | 1 + .../usages/impl/SearchForUsagesRunnable.java | 12 +- .../usages/impl/UsagePreviewPanel.java | 27 +- .../intellij/usages/impl/UsageViewImpl.java | 9 +- 8 files changed, 242 insertions(+), 81 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindPopupPanel.java b/platform/lang-impl/src/com/intellij/find/impl/FindPopupPanel.java index 8cbc0f899b66..ee4604def609 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindPopupPanel.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindPopupPanel.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.AutoPopupController; import com.intellij.find.*; import com.intellij.find.actions.ShowUsagesAction; import com.intellij.find.editorHeaderActions.ShowMoreOptions; +import com.intellij.find.replaceInProject.ReplaceInProjectManager; import com.intellij.icons.AllIcons; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.ui.UISettings; @@ -30,6 +31,7 @@ import com.intellij.openapi.actionSystem.impl.ActionButton; import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.editor.ex.EditorEx; @@ -65,6 +67,7 @@ import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.table.JBTable; +import com.intellij.usageView.UsageInfo; import com.intellij.usages.FindUsagesProcessPresentation; import com.intellij.usages.Usage; import com.intellij.usages.UsageInfo2UsageAdapter; @@ -85,9 +88,7 @@ import javax.swing.table.DefaultTableModel; import javax.swing.text.JTextComponent; import java.awt.*; import java.awt.event.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashSet; +import java.util.*; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -130,6 +131,8 @@ public class FindPopupPanel extends JBPanel implements FindUI { private ActionButton myTabResultsButton; private ActionButton myPinButton; private JButton myOKButton; + private JButton myReplaceAllButton; + private JButton myReplaceSelectedButton; private JTextArea mySearchComponent; private JTextArea myReplaceComponent; private String mySelectedContextName = FindBundle.message("find.context.anywhere.scope.label"); @@ -142,6 +145,7 @@ public class FindPopupPanel extends JBPanel implements FindUI { private LoadingDecorator myLoadingDecorator; private int myLoadingHash; private JPanel myTitlePanel; + private String[] myMessageState = new String[2]; FindPopupPanel(@NotNull FindUIHelper helper) { myHelper = helper; @@ -413,37 +417,70 @@ public class FindPopupPanel extends JBPanel implements FindUI { } }.registerCustomShortcutSet(CustomShortcutSet.fromString("alt DOWN"), this); myOKButton = new JButton(FindBundle.message("find.popup.find.button")); + myReplaceAllButton = new JButton(FindBundle.message("find.popup.replace.all.button")); + myReplaceSelectedButton = new JButton(FindBundle.message("find.popup.replace.selected.button", 0)); + myReplaceSelectedButton.setToolTipText("Replace " + KeymapUtil.getKeystrokeText(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.ALT_DOWN_MASK))); + myOkActionListener = __ -> { - FindModel validateModel = myHelper.getModel().clone(); - applyTo(validateModel, false); - - ValidationInfo validationInfo = getValidationInfo(validateModel); - - if (validationInfo == null) { - myHelper.getModel().copyFrom(validateModel); - myHelper.updateFindSettings(); - myHelper.doOKAction(); - } - else { - String message = validationInfo.message; - Messages.showMessageDialog( - this, - message, - CommonBundle.getErrorTitle(), - Messages.getErrorIcon() - ); - return; - } - myIsPinned.set(false); - Disposer.dispose(myBalloon); + doOK(true); }; + myReplaceAllButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (myResultsPreviewTable.getRowCount() < 2 + || JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(FindPopupPanel.this, + FindBundle.message( + "find.replace.all.confirmation", + myMessageState[0], + getStringToFind(), + myMessageState[1], + getStringToReplace()), + FindBundle.message( + "find.replace.all.confirmation.title"), + JOptionPane.OK_CANCEL_OPTION)) { + doOK(false); + } + } + }); + myReplaceSelectedButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int rowToSelect = myResultsPreviewTable.getSelectionModel().getMinSelectionIndex(); + Map usages = getSelectedUsages(); + if (usages == null) { + return; + } + CommandProcessor.getInstance().executeCommand(myProject, () -> { + for (Map.Entry entry : usages.entrySet()) { + try { + ReplaceInProjectManager.getInstance(myProject).replaceUsage(entry.getValue(), myHelper.getModel(), Collections.emptySet(), false); + ((DefaultTableModel)myResultsPreviewTable.getModel()).removeRow(entry.getKey()); + } + catch (FindManager.MalformedReplacementStringException ex) { + if (!ApplicationManager.getApplication().isUnitTestMode()) { + Messages.showErrorDialog(FindPopupPanel.this, ex.getMessage(), FindBundle.message("find.replace.invalid.replacement.string.title")); + } + break; + } + } + + + ApplicationManager.getApplication().invokeLater(() -> { + if (myResultsPreviewTable.getRowCount() > rowToSelect) { + myResultsPreviewTable.getSelectionModel().setSelectionInterval(rowToSelect, rowToSelect); + } + ScrollingUtil.ensureSelectionExists(myResultsPreviewTable); + }); + }, FindBundle.message("find.replace.command"), null); + } + }); myOKButton.addActionListener(myOkActionListener); boolean enterAsOK = Registry.is("ide.find.enter.as.ok", false); - new AnAction() { + new DumbAwareAction() { @Override public void actionPerformed(AnActionEvent e) { - if (enterAsOK || myHelper.isReplaceState()) { + if (enterAsOK ) { myOkActionListener.actionPerformed(null); } else { navigateToSelectedUsage(); @@ -453,7 +490,6 @@ public class FindPopupPanel extends JBPanel implements FindUI { new AnAction() { @Override public void actionPerformed(AnActionEvent e) { - if (myHelper.isReplaceState()) return; if (enterAsOK) { navigateToSelectedUsage(); } else { @@ -529,7 +565,7 @@ public class FindPopupPanel extends JBPanel implements FindUI { myResultsPreviewTable.setFocusable(false); myResultsPreviewTable.getEmptyText().setShowAboveCenter(false); myResultsPreviewTable.setShowColumns(false); - myResultsPreviewTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + myResultsPreviewTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); myResultsPreviewTable.setShowGrid(false); myResultsPreviewTable.setIntercellSpacing(JBUI.emptySize()); new DoubleClickListener() { @@ -550,6 +586,7 @@ public class FindPopupPanel extends JBPanel implements FindUI { myResultsPreviewTable); ScrollingUtil.installActions(myResultsPreviewTable, false, mySearchComponent); ScrollingUtil.installActions(myResultsPreviewTable, false, myReplaceComponent); + ScrollingUtil.installActions(myResultsPreviewTable, false, myReplaceSelectedButton); ActionListener helpAction = __ -> HelpManager.getInstance().invokeHelp("reference.dialogs.findinpath"); registerKeyboardAction(helpAction,KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0),JComponent.WHEN_IN_FOCUSED_WINDOW); @@ -562,27 +599,31 @@ public class FindPopupPanel extends JBPanel implements FindUI { } }; Disposer.register(myDisposable, myUsagePreviewPanel); - myResultsPreviewTable.getSelectionModel().addListSelectionListener(e -> { - if (e.getValueIsAdjusting()) return; - int index = myResultsPreviewTable.getSelectedRow(); - if (index != -1) { - UsageInfo2UsageAdapter adapter = (UsageInfo2UsageAdapter)myResultsPreviewTable.getModel().getValueAt(index, 0); - myUsagePreviewPanel.updateLayout(adapter.isValid() ? Arrays.asList(adapter.getMergedInfos()) : null); - VirtualFile file = adapter.getFile(); - String path = ""; - if (file != null) { - String relativePath = VfsUtilCore.getRelativePath(file, myProject.getBaseDir()); - if (relativePath == null) relativePath = file.getPath(); - path = "   " + - relativePath - .replace(file.getName(), "" + file.getName() + "") + ""; + Runnable updatePreviewRunnable = () -> { + if (Disposer.isDisposed(myDisposable)) return; + int[] selectedRows = myResultsPreviewTable.getSelectedRows(); + final List selection = new SmartList<>(); + VirtualFile file = null; + for (int row : selectedRows) { + UsageInfo2UsageAdapter adapter = (UsageInfo2UsageAdapter)myResultsPreviewTable.getModel().getValueAt(row, 0); + file = adapter.getFile(); + if (adapter.isValid()) { + selection.addAll(Arrays.asList(adapter.getMergedInfos())); } - myUsagePreviewPanel.setBorder(IdeBorderFactory.createTitledBorder(path, false, new JBInsets(8, 0, 0, 0)).setShowLine(false)); + } + String title = getTitle(file); + myReplaceSelectedButton.setText(FindBundle.message("find.popup.replace.selected.button", selection.size())); + myUsagePreviewPanel.updateLayout(selection); + if (myUsagePreviewPanel.getCannotPreviewMessage(selection) == null && title != null) { + myUsagePreviewPanel.setBorder(IdeBorderFactory.createTitledBorder(title, false, new JBInsets(8, 0, 0, 0)).setShowLine(false)); } else { - myUsagePreviewPanel.updateLayout(null); - myUsagePreviewPanel.setBorder(IdeBorderFactory.createBorder()); + myUsagePreviewPanel.setBorder(JBUI.Borders.empty()); } + }; + myResultsPreviewTable.getSelectionModel().addListSelectionListener(e -> { + if (e.getValueIsAdjusting()) return; + ApplicationManager.getApplication().invokeLater(updatePreviewRunnable); }); mySearchRescheduleOnCancellationsAlarm = new Alarm(); @@ -607,6 +648,8 @@ public class FindPopupPanel extends JBPanel implements FindUI { myOKHintLabel.setEnabled(false); bottomPanel.add(myOKHintLabel, "gapright 10"); bottomPanel.add(myOKButton); + bottomPanel.add(myReplaceAllButton); + bottomPanel.add(myReplaceSelectedButton); myCodePreviewComponent = myUsagePreviewPanel.createComponent(); splitter.setSecondComponent(myCodePreviewComponent); @@ -654,6 +697,40 @@ public class FindPopupPanel extends JBPanel implements FindUI { }); } + private void doOK(boolean promptOnReplace) { + FindModel validateModel = myHelper.getModel().clone(); + applyTo(validateModel, false); + + ValidationInfo validationInfo = getValidationInfo(validateModel); + + if (validationInfo == null) { + myHelper.getModel().copyFrom(validateModel); + myHelper.getModel().setPromptOnReplace(promptOnReplace); + myHelper.updateFindSettings(); + myHelper.doOKAction(); + } + else { + String message = validationInfo.message; + Messages.showMessageDialog( + this, + message, + CommonBundle.getErrorTitle(), + Messages.getErrorIcon() + ); + return; + } + myIsPinned.set(false); + Disposer.dispose(myBalloon); + } + + @Nullable + private String getTitle(@Nullable VirtualFile file) { + if (file == null) return null; + String path = VfsUtilCore.getRelativePath(file, myProject.getBaseDir()); + if (path == null) path = file.getPath(); + return "   " + path.replace(file.getName(), "" + file.getName() + "") + ""; + } + @NotNull private static StateRestoringCheckBox createCheckBox(String message) { StateRestoringCheckBox checkBox = new StateRestoringCheckBox(FindBundle.message(message)); @@ -737,7 +814,7 @@ public class FindPopupPanel extends JBPanel implements FindUI { } else { myOKHintLabel.setText(KeymapUtil.getKeystrokeText(ENTER_WITH_MODIFIERS)); } - myOKButton.setText(FindBundle.message(isReplaceState ? "find.popup.replace.button" : "find.popup.find.button")); + myOKButton.setText(FindBundle.message("find.popup.find.button")); } private void updateControls() { @@ -765,6 +842,8 @@ public class FindPopupPanel extends JBPanel implements FindUI { myCbCaseSensitive.makeSelectable(); } } + myReplaceAllButton.setVisible(myHelper.isReplaceState()); + myReplaceSelectedButton.setVisible(myHelper.isReplaceState()); } private void updateScopeDetailsPanel() { @@ -852,14 +931,16 @@ public class FindPopupPanel extends JBPanel implements FindUI { myHelper.myPreviousModel = myHelper.getModel().clone(); + myReplaceAllButton.setEnabled(false); + myReplaceSelectedButton.setEnabled(false); + myReplaceSelectedButton.setText(FindBundle.message("find.popup.replace.selected.button", 0)); myCodePreviewComponent.setVisible(false); mySearchTextArea.setInfoText(null); myResultsPreviewTable.setModel(model); if (result != null) { - myResultsPreviewTable.getEmptyText().setText(UIBundle.message("message.nothingToShow") + " ("+result.message+")"); - onStop(hash); + onStop(hash, result.message); return; } @@ -867,7 +948,6 @@ public class FindPopupPanel extends JBPanel implements FindUI { FindInProjectUtil.getScopeFromModel(myProject, myHelper.myPreviousModel), myProject); myResultsPreviewTable.getColumnModel().getColumn(0).setCellRenderer( new FindDialog.UsageTableCellRenderer(myCbFileFilter.isSelected(), false, scope)); - myResultsPreviewTable.getEmptyText().setText("Searching..."); onStart(hash); final AtomicInteger resultsCount = new AtomicInteger(); @@ -925,20 +1005,26 @@ public class FindPopupPanel extends JBPanel implements FindUI { } int occurrences = resultsCount.get(); int filesWithOccurrences = resultsFilesCount.get(); - if (occurrences == 0) myResultsPreviewTable.getEmptyText().setText(UIBundle.message("message.nothingToShow")); myCodePreviewComponent.setVisible(occurrences > 0); + myReplaceAllButton.setEnabled(occurrences > 0); + myReplaceSelectedButton.setEnabled(occurrences > 0); + StringBuilder stringBuilder = new StringBuilder(); if (occurrences > 0) { stringBuilder.append(Math.min(ShowUsagesAction.getUsagesPageSize(), occurrences)); boolean foundAllUsages = occurrences < ShowUsagesAction.getUsagesPageSize(); + myMessageState[0] = String.valueOf(occurrences); if (!foundAllUsages) { stringBuilder.append("+"); + myMessageState[0] += "+"; } stringBuilder.append(UIBundle.message("message.matches", occurrences)); stringBuilder.append(" in "); stringBuilder.append(filesWithOccurrences); + myMessageState[1] = String.valueOf(filesWithOccurrences); if (!foundAllUsages) { stringBuilder.append("+"); + myMessageState[1] += "+"; } stringBuilder.append(UIBundle.message("message.files", filesWithOccurrences)); } @@ -976,14 +1062,23 @@ public class FindPopupPanel extends JBPanel implements FindUI { private void onStart(int hash) { myLoadingHash = hash; myLoadingDecorator.startLoading(false); + myResultsPreviewTable.getEmptyText().setText("Searching..."); } private void onStop(int hash) { + onStop(hash, null); + } + + private void onStop(int hash, String message) { if (hash != myLoadingHash) { return; } - UIUtil.invokeLaterIfNeeded(() -> myLoadingDecorator.stopLoading()); + UIUtil.invokeLaterIfNeeded(() -> { + myResultsPreviewTable.getEmptyText().setText(message != null ? UIBundle.message("message.nothingToShow.with.problem", message) + : UIBundle.message("message.nothingToShow")); + myLoadingDecorator.stopLoading(); + }); } @Override @@ -1072,14 +1167,10 @@ public class FindPopupPanel extends JBPanel implements FindUI { model.setSearchContext(searchContext); model.setRegularExpressions(myCbRegularExpressions.isSelected()); - String stringToFind = getStringToFind(); - model.setStringToFind(stringToFind); + model.setStringToFind(getStringToFind()); if (model.isReplaceState()) { - model.setPromptOnReplace(true); - model.setReplaceAll(false); - String stringToReplace = getStringToReplace(); - model.setStringToReplace(StringUtil.convertLineSeparators(stringToReplace)); + model.setStringToReplace(StringUtil.convertLineSeparators(getStringToReplace())); } @@ -1098,29 +1189,35 @@ public class FindPopupPanel extends JBPanel implements FindUI { } private void navigateToSelectedUsage() { - Usage[] usages = getSelectedUsages(); + Map usages = getSelectedUsages(); if (usages != null) { applyTo(FindManager.getInstance(myProject).getFindInProjectModel(), false); myBalloon.cancel(); - - usages[0].navigate(true); - for (int i = 1; i < usages.length; ++i) usages[i].highlightInEditor(); + boolean first = true; + for (Usage usage : usages.values()) { + if (first) { + usage.navigate(true); + } + else { + usage.highlightInEditor(); + } + first = false; + } } } @Nullable - private Usage[] getSelectedUsages() { + private Map getSelectedUsages() { int[] rows = myResultsPreviewTable.getSelectedRows(); - List usages = null; + Map result = null; for (int row : rows) { Object valueAt = myResultsPreviewTable.getModel().getValueAt(row, 0); if (valueAt instanceof Usage) { - if (usages == null) usages = new SmartList<>(); - Usage at = (Usage)valueAt; - usages.add(at); + if (result == null) result = ContainerUtil.newLinkedHashMap(); + result.put(row, (Usage)valueAt); } } - return usages != null ? ContainerUtil.toArray(usages, Usage.EMPTY_ARRAY) : null; + return result; } public static ActionToolbarImpl createToolbar(AnAction... actions) { diff --git a/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java b/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java index e0ef589cd2ef..98e5be1a6462 100644 --- a/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java +++ b/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java @@ -152,6 +152,7 @@ public class ReplaceInProjectManager { final UsageViewPresentation presentation = FindInProjectUtil.setupViewPresentation(findModel.isOpenInNewTab(), findModelCopy); final FindUsagesProcessPresentation processPresentation = FindInProjectUtil.setupProcessPresentation(myProject, true, presentation); + processPresentation.setShowFindOptionsPrompt(findModel.isPromptOnReplace()); UsageSearcherFactory factory = new UsageSearcherFactory(findModelCopy, processPresentation); searchAndShowUsages(manager, factory, findModelCopy, presentation, processPresentation, findManager); @@ -214,9 +215,9 @@ public class ReplaceInProjectManager { @Override public void findingUsagesFinished(final UsageView usageView) { - if (context[0] != null && findManager.getFindInProjectModel().isPromptOnReplace()) { + if (context[0] != null && !processPresentation.isShowFindOptionsPrompt()) { TransactionGuard.submitTransaction(myProject, () -> { - replaceWithPrompt(context[0]); + replaceUsagesUnderCommand(context[0], usageView.getUsages()); context[0].invalidateExcludedSetCache(); }); } @@ -354,7 +355,27 @@ public class ReplaceInProjectManager { } private void addReplaceActions(final ReplaceContext replaceContext) { - final Runnable replaceRunnable = () -> replaceUsagesUnderCommand(replaceContext, replaceContext.getUsageView().getUsages()); + final Runnable replaceRunnable = () -> { + Set usages = replaceContext.getUsageView().getUsages(); + Set files = new HashSet<>(); + if (usages.isEmpty()) return; + for (Usage usage : usages) { + if (usage instanceof UsageInfo2UsageAdapter) { + files.add(((UsageInfo2UsageAdapter)usage).getFile()); + } + } + if (files.size() < 2 || + JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(replaceContext.getUsageView().getComponent(), + FindBundle.message("find.replace.all.confirmation", + usages.size(), + replaceContext.getFindModel().getStringToFind(), + files.size(), + replaceContext.getFindModel().getStringToReplace()), + FindBundle.message("find.replace.all.confirmation.title"), + JOptionPane.OK_CANCEL_OPTION)) { + replaceUsagesUnderCommand(replaceContext, usages); + } + }; replaceContext.getUsageView().addButtonToLowerPane(replaceRunnable, FindBundle.message("find.replace.all.action")); final Runnable replaceSelectedRunnable = diff --git a/platform/platform-resources-en/src/messages/FindBundle.properties b/platform/platform-resources-en/src/messages/FindBundle.properties index 369638e3ac0e..79295aa4ea17 100644 --- a/platform/platform-resources-en/src/messages/FindBundle.properties +++ b/platform/platform-resources-en/src/messages/FindBundle.properties @@ -24,7 +24,8 @@ find.what.usages.of.classes.and.interfaces=Usages of &classes and interfaces find.dialog.find.button=Find find.popup.find.button=Open in Find Window -find.popup.replace.button=Replace in Find Window... +find.popup.replace.all.button=Repl&ace All +find.popup.replace.selected.button=&Replace{0,choice,0#|1#|2# {0} occurrences} find.usages.in.file.dialog.title=Find Usages in File find.usages.dialog.title=Find Usages find.open.in.new.tab.checkbox=Open in new ta&b @@ -71,7 +72,7 @@ find.all.button=Find &All find.text.to.find.label=Text to &find: find.replace.with.label=Replace &with: find.filter.file.name.group=File name filter -find.filter.file.mask.checkbox=File m&ask(s) +find.filter.file.mask.checkbox=File mas&k(s) find.context.combo.label=Conte&xt:\u0020 find.context.anywhere.scope.label=Anywhere find.context.in.comments.scope.label=In Comments @@ -129,9 +130,11 @@ find.replace.occurrences.found.in.read.only.files.status=Occurrences found in re find.replace.select.on.editor.command=Select on Editor find.replace.found.usage.title=Replace Usage {0} of {1} Found - {2} find.replace.command=Replace -find.replace.all.action=Replace All +find.replace.all.action=&Replace All find.replace.all.action.description=&Do Replace Al&&l -find.replace.selected.action=Rep&lace &&Selected +find.replace.all.confirmation=Replace {0} ocurrences of ''{1}'' across
{2} files with ''{3}''? +find.replace.all.confirmation.title=Replace All +find.replace.selected.action=Repl&ace Selected find.replace.occurrences.in.read.only.files.prompt=Occurrences found in read-only files.\nThe operation will not affect them.\nContinue anyway? find.replace.occurrences.in.read.only.files.title=Read-only Files Found find.scope.custom.radio=Cu&stom: @@ -142,7 +145,7 @@ occurrence=occurrence results.options.group=Result options find.popup.case.sensitive=Match &case -find.popup.whole.words=Wo&rds +find.popup.whole.words=W&ords find.popup.regex=Re&gex find.popup.filemask=File m&ask: find.popup.scope.project=In &Project diff --git a/platform/platform-resources-en/src/messages/UIBundle.properties b/platform/platform-resources-en/src/messages/UIBundle.properties index 927918503891..cb3c18effca2 100644 --- a/platform/platform-resources-en/src/messages/UIBundle.properties +++ b/platform/platform-resources-en/src/messages/UIBundle.properties @@ -168,6 +168,7 @@ file.chooser.save.dialog.file.name=File name: tool.window.name.documentation=Documentation message.nothingToShow=Nothing to show +message.nothingToShow.with.problem=Nothing to show ({0}) message.noMatchesFound=No matches found message.matches={0,choice, 0# matches|1# match|2# matches} message.files={0,choice, 0# files|1# file|2# files} diff --git a/platform/platform-resources-en/src/messages/UsageView.properties b/platform/platform-resources-en/src/messages/UsageView.properties index 6b042964e119..4646be33b01b 100644 --- a/platform/platform-resources-en/src/messages/UsageView.properties +++ b/platform/platform-resources-en/src/messages/UsageView.properties @@ -60,6 +60,7 @@ usage.type.read=Value read usage.type.write=Value write preview.usages.action.text=Preview {0} select.the.usage.to.preview=Select {0} to preview +several.occurrences.selected=
Several files selected.
Select occurrences from one file to preview.
usages.were.filtered.out={0,number} {0,choice, 1#usage was|2#usages were} filtered out show.usages.only.usage=It''s the only usage in {0} all.usages.are.in.this.line=All {0} usages in {1} are in this line diff --git a/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java b/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java index 6adeba04e3e3..5602359ec783 100644 --- a/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java +++ b/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java @@ -311,7 +311,17 @@ class SearchForUsagesRunnable implements Runnable { usageView = new UsageViewImpl(myProject, myPresentation, mySearchFor, mySearcherFactory); usageView.associateProgress(indicator); if (myUsageViewRef.compareAndSet(null, usageView)) { - openView(usageView); + if (myProcessPresentation.isShowFindOptionsPrompt()) { + openView(usageView); + } else { + UsageViewImpl[] tmp = new UsageViewImpl[]{usageView}; + SwingUtilities.invokeLater(() -> { + if (myProject.isDisposed()) return; + if (myListener != null) { + myListener.usageViewCreated(tmp[0]); + } + }); + } final Usage firstUsage = myFirstUsage.get(); if (firstUsage != null) { final UsageViewImpl finalUsageView = usageView; diff --git a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java index 8aab82863983..25143f3a0bbf 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java @@ -221,14 +221,35 @@ public class UsagePreviewPanel extends UsageContextPanelBase implements DataProv } } - + @Nullable + public final String getCannotPreviewMessage(@Nullable final List infos) { + if (infos == null || infos.isEmpty()) { + return UsageViewBundle.message("select.the.usage.to.preview", myPresentation.getUsagesWord()); + } else { + PsiFile psiFile = null; + for (UsageInfo info : infos) { + PsiElement element = info.getElement(); + if (element == null) continue; + PsiFile file = element.getContainingFile(); + if (psiFile == null) { + psiFile = file; + } else { + if (psiFile != file) { + return UsageViewBundle.message("several.occurrences.selected"); + } + } + } + } + return null; + } @Override public void updateLayoutLater(@Nullable final List infos) { - if (infos == null) { + String cannotPreviewMessage = getCannotPreviewMessage(infos); + if (cannotPreviewMessage != null) { releaseEditor(); removeAll(); - JComponent titleComp = new JLabel(UsageViewBundle.message("select.the.usage.to.preview", myPresentation.getUsagesWord()), SwingConstants.CENTER); + JComponent titleComp = new JLabel(cannotPreviewMessage, SwingConstants.CENTER); add(titleComp, BorderLayout.CENTER); revalidate(); } diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index ad83b2b58a15..b2129a00a068 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -1098,6 +1098,7 @@ public class UsageViewImpl implements UsageView { @Override public void removeUsagesBulk(@NotNull Collection usages) { + int selectionRow = myTree.getMinSelectionRow(); Set nodes = usagesToNodes(usages.stream()).collect(Collectors.toSet()); usages.forEach(u -> myUsageNodes.remove(u)); @@ -1106,6 +1107,10 @@ public class UsageViewImpl implements UsageView { if (isDisposed) return; DefaultTreeModel treeModel = (DefaultTreeModel)myTree.getModel(); ((GroupNode)treeModel.getRoot()).removeUsagesBulk(nodes, treeModel); + int rowToSelect = Math.min(myTree.getRowCount() - 1, selectionRow); + if (rowToSelect >=0) { + myTree.setSelectionRow(rowToSelect); + } }); } } @@ -1240,7 +1245,9 @@ public class UsageViewImpl implements UsageView { @Override public void close() { cancelCurrentSearch(); - UsageViewManager.getInstance(myProject).closeContent(myContent); + if (myContent != null) { + UsageViewManager.getInstance(myProject).closeContent(myContent); + } } private void saveSplitterProportions() {