diff options
Diffstat (limited to 'platform/lang-impl/src')
123 files changed, 3025 insertions, 1737 deletions
diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java index f978d5f6dcdb..59736bc0e4b2 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java @@ -18,6 +18,7 @@ package com.intellij.application.options.codeStyle; import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.codeStyle.CustomCodeStyleSettings; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TreeTableSpeedSearch; @@ -378,7 +379,7 @@ public abstract class OptionTableWithPreviewPanel extends MultilanguageCodeStyle this.groupName = groupName; try { - Class styleSettingsClass = clazz == null ? CodeStyleSettings.class : clazz; + Class styleSettingsClass = clazz == null ? CommonCodeStyleSettings.class : clazz; this.field = styleSettingsClass.getField(fieldName); } catch (NoSuchFieldException e) { diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/RightMarginForm.form b/platform/lang-impl/src/com/intellij/application/options/codeStyle/RightMarginForm.form new file mode 100644 index 000000000000..67f579a77ff5 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/RightMarginForm.form @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.application.options.codeStyle.RightMarginForm"> + <grid id="27dc6" binding="myTopPanel" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <margin top="0" left="0" bottom="0" right="0"/> + <constraints> + <xy x="20" y="20" width="500" height="400"/> + </constraints> + <properties/> + <border type="none"/> + <children> + <component id="12cf8" class="com.intellij.ui.components.JBLabel"> + <constraints> + <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text resource-bundle="messages/ApplicationBundle" key="editbox.right.margin.columns"/> + </properties> + </component> + <vspacer id="1265c"> + <constraints> + <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> + </constraints> + </vspacer> + <component id="78fe" class="javax.swing.JTextField" binding="myRightMarginField"> + <constraints> + <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"> + <preferred-size width="50" height="-1"/> + </grid> + </constraints> + <properties> + <horizontalAlignment value="2"/> + </properties> + </component> + <component id="5ef52" class="javax.swing.JCheckBox" binding="myDefaultGeneralCheckBox" default-binding="true"> + <constraints> + <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text resource-bundle="messages/ApplicationBundle" key="settings.code.style.default.general"/> + </properties> + </component> + </children> + </grid> +</form> diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/RightMarginForm.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/RightMarginForm.java new file mode 100644 index 000000000000..70c747dc11ca --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/RightMarginForm.java @@ -0,0 +1,120 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.application.options.codeStyle; + +import com.intellij.lang.Language; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * Can be used for languages which do not use standard "Wrapping and Braces" panel. + * <p> + * <strong>Note</strong>: besides adding the panel to UI it is necessary to make sure that language's own + * <code>LanguageCodeStyleSettingsProvider</code> explicitly supports RIGHT_MARGIN field in <code>customizeSettings()</code> + * method as shown below: + * <pre> + * public void customizeSettings(...) { + * if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) { + * consumer.showStandardOptions("RIGHT_MARGIN"); + * } + * } + * </pre> + * @author Rustam Vishnyakov + */ +public class RightMarginForm { + private JTextField myRightMarginField; + private JCheckBox myDefaultGeneralCheckBox; + private JPanel myTopPanel; + private final Language myLanguage; + private final int myDefaultRightMargin; + + public RightMarginForm(@NotNull Language language, @NotNull CodeStyleSettings settings) { + myLanguage = language; + myDefaultRightMargin = settings.RIGHT_MARGIN; + myDefaultGeneralCheckBox.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (myDefaultGeneralCheckBox.isSelected()) { + myRightMarginField.setText(Integer.toString(myDefaultRightMargin)); + myRightMarginField.setEnabled(false); + } + else { + myRightMarginField.setEnabled(true); + } + } + }); + } + + public void reset(@NotNull CodeStyleSettings settings) { + CommonCodeStyleSettings langSettings = settings.getCommonSettings(myLanguage); + if (langSettings != settings && langSettings.RIGHT_MARGIN >= 0) { + myDefaultGeneralCheckBox.setSelected(false); + myRightMarginField.setText(Integer.toString(langSettings.RIGHT_MARGIN)); + } + else { + myDefaultGeneralCheckBox.setSelected(true); + myRightMarginField.setText(Integer.toString(settings.RIGHT_MARGIN)); + if (langSettings == settings) { + myDefaultGeneralCheckBox.setEnabled(false); + myRightMarginField.setEnabled(false); + } + } + } + + public void apply(@NotNull CodeStyleSettings settings) { + CommonCodeStyleSettings langSettings = settings.getCommonSettings(myLanguage); + if (langSettings != settings) { + if (myDefaultGeneralCheckBox.isSelected()) { + langSettings.RIGHT_MARGIN = -1; + } + else { + langSettings.RIGHT_MARGIN = getFieldRightMargin(settings.RIGHT_MARGIN); + } + } + } + + public boolean isModified(@NotNull CodeStyleSettings settings) { + CommonCodeStyleSettings langSettings = settings.getCommonSettings(myLanguage); + if (myDefaultGeneralCheckBox.isSelected()) { + return langSettings.RIGHT_MARGIN >= 0; + } + else { + return langSettings.RIGHT_MARGIN != getFieldRightMargin(settings.RIGHT_MARGIN); + } + } + + private int getFieldRightMargin(int fallBackValue) { + String strValue = myRightMarginField.getText(); + if (!strValue.trim().isEmpty()) { + try { + return Integer.parseInt(strValue); + } + catch (NumberFormatException e) { + myRightMarginField.setText(Integer.toString(fallBackValue)); + } + } + return fallBackValue; + } + + public JPanel getTopPanel() { + return myTopPanel; + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/match/ArrangementMatchingRulesControl.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/match/ArrangementMatchingRulesControl.java index fa14958132d0..145ae1094e0b 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/match/ArrangementMatchingRulesControl.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/match/ArrangementMatchingRulesControl.java @@ -165,7 +165,7 @@ public class ArrangementMatchingRulesControl extends JBTable { } final List<ArrangementSectionRule> result = ContainerUtil.newArrayList(); - final List<StdArrangementMatchRule> currentRules = ContainerUtil.newArrayList(); + final List<StdArrangementMatchRule> buffer = ContainerUtil.newArrayList(); String currentSectionStart = null; for (int i = 0; i < getModel().getSize(); i++) { Object element = getModel().getElementAt(i); @@ -174,15 +174,12 @@ public class ArrangementMatchingRulesControl extends JBTable { mySectionRuleManager == null ? null : mySectionRuleManager.getSectionRuleData((StdArrangementMatchRule)element); if (sectionRule != null) { if (sectionRule.isSectionStart()) { - if (currentSectionStart != null) { - result.add(ArrangementSectionRule.create(currentSectionStart, null, currentRules)); - currentRules.clear(); - } + appendBufferedSectionRules(result, buffer, currentSectionStart); currentSectionStart = sectionRule.getText(); } else { - result.add(ArrangementSectionRule.create(StringUtil.notNullize(currentSectionStart), sectionRule.getText(), currentRules)); - currentRules.clear(); + result.add(ArrangementSectionRule.create(StringUtil.notNullize(currentSectionStart), sectionRule.getText(), buffer)); + buffer.clear(); currentSectionStart = null; } } @@ -190,17 +187,34 @@ public class ArrangementMatchingRulesControl extends JBTable { result.add(ArrangementSectionRule.create((StdArrangementMatchRule)element)); } else { - currentRules.add((StdArrangementMatchRule)element); + buffer.add((StdArrangementMatchRule)element); } } } - if (currentSectionStart != null) { - result.add(ArrangementSectionRule.create(currentSectionStart, null, currentRules)); - } + appendBufferedSectionRules(result, buffer, currentSectionStart); return result; } + private static void appendBufferedSectionRules(@NotNull List<ArrangementSectionRule> result, + @NotNull List<StdArrangementMatchRule> buffer, + @Nullable String currentSectionStart) { + if (currentSectionStart == null) { + return; + } + + if (buffer.isEmpty()) { + result.add(ArrangementSectionRule.create(currentSectionStart, null)); + } + else { + result.add(ArrangementSectionRule.create(currentSectionStart, null, buffer.get(0))); + for (int j = 1; j < buffer.size(); j++) { + result.add(ArrangementSectionRule.create(buffer.get(j))); + } + buffer.clear(); + } + } + @Override protected void processMouseEvent(MouseEvent e) { int id = e.getID(); diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form index 9872c7c624aa..db23ce17b2c3 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form @@ -3,7 +3,7 @@ <grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <xy x="20" y="20" width="500" height="527"/> + <xy x="20" y="20" width="500" height="585"/> </constraints> <properties/> <border type="none"/> @@ -108,7 +108,7 @@ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> </constraints> </vspacer> - <grid id="eb8d0" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="10"> + <grid id="eb8d0" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="10"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> @@ -119,7 +119,7 @@ </clientProperties> <border type="none" title-resource-bundle="messages/ApplicationBundle" title-key="group.tab.closing.policy"/> <children> - <grid id="5a5a7" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="5a5a7" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> @@ -129,7 +129,9 @@ <children> <component id="527a6" class="javax.swing.JLabel"> <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"> + <preferred-size width="60" height="27"/> + </grid> </constraints> <properties> <text resource-bundle="messages/ApplicationBundle" key="editbox.tab.limit"/> @@ -138,19 +140,38 @@ <component id="16477" class="javax.swing.JTextField" binding="myEditorTabLimitField"> <constraints> <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false"> - <preferred-size width="30" height="-1"/> + <preferred-size width="30" height="27"/> </grid> </constraints> <properties> <text value="15"/> </properties> </component> + <component id="2479d" class="javax.swing.JLabel"> + <constraints> + <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text resource-bundle="messages/ApplicationBundle" key="editbox.tab.title.limit"/> + </properties> + </component> + <component id="3499c" class="javax.swing.JTextField" binding="myTabTitleLimitField"> + <constraints> + <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false"> + <preferred-size width="30" height="27"/> + </grid> + </constraints> + <properties> + <columns value="2"/> + <text value="30"/> + </properties> + </component> </children> </grid> <grid id="9b723" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="2"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <border type="none"/> @@ -184,7 +205,7 @@ <grid id="611cc" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="2"> <margin top="10" left="0" bottom="0" right="0"/> <constraints> - <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <border type="none"/> diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java index 4e1de817255a..35a34085d035 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java @@ -45,6 +45,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { private JCheckBox myShowCloseButtonOnCheckBox; private JCheckBox myShowDirectoryInTabCheckBox; private JRadioButton myActivateRightNeighbouringTabRadioButton; + private JTextField myTabTitleLimitField; public EditorTabsConfigurable() { myEditorTabPlacement.setModel(new DefaultComboBoxModel(new Object[]{ @@ -119,6 +120,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { myHideKnownExtensions.setSelected(uiSettings.HIDE_KNOWN_EXTENSION_IN_TABS); myShowDirectoryInTabCheckBox.setSelected(uiSettings.SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES); myEditorTabLimitField.setText(Integer.toString(uiSettings.EDITOR_TAB_LIMIT)); + myTabTitleLimitField.setText(Integer.toString(uiSettings.EDITOR_TAB_TITLE_LIMIT)); myShowCloseButtonOnCheckBox.setSelected(uiSettings.SHOW_CLOSE_BUTTON); if (uiSettings.CLOSE_NON_MODIFIED_FILES_FIRST) { @@ -171,14 +173,29 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { uiSettings.ACTIVATE_RIGHT_EDITOR_ON_CLOSE = myActivateRightNeighbouringTabRadioButton.isSelected(); String temp = myEditorTabLimitField.getText(); - if(temp.trim().length() > 0){ + if (temp.trim().length() > 0) { try { int newEditorTabLimit = Integer.parseInt(temp); - if(newEditorTabLimit>0&&newEditorTabLimit!=uiSettings.EDITOR_TAB_LIMIT){ - uiSettings.EDITOR_TAB_LIMIT=newEditorTabLimit; + if (newEditorTabLimit > 0 && newEditorTabLimit != uiSettings.EDITOR_TAB_LIMIT) { + uiSettings.EDITOR_TAB_LIMIT = newEditorTabLimit; uiSettingsChanged = true; } - }catch (NumberFormatException ignored){} + } + catch (NumberFormatException ignored) { + } + } + temp = myTabTitleLimitField.getText(); + if (temp.trim().length() > 0) { + try { + int newTabTitleLimit = Integer.parseInt(temp); + newTabTitleLimit = Math.max(25, Math.min(100, newTabTitleLimit)); + if (newTabTitleLimit != uiSettings.EDITOR_TAB_TITLE_LIMIT){ + uiSettings.EDITOR_TAB_TITLE_LIMIT = newTabTitleLimit; + uiSettingsChanged = true; + } + } + catch (NumberFormatException ignored) { + } } if(uiSettingsChanged){ uiSettings.fireUISettingsChanged(); @@ -191,6 +208,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { boolean isModified = isModified(myCbModifiedTabsMarkedWithAsterisk, uiSettings.MARK_MODIFIED_TABS_WITH_ASTERISK); isModified |= isModified(myShowTabsTooltipsCheckBox, uiSettings.SHOW_TABS_TOOLTIPS); isModified |= isModified(myEditorTabLimitField, uiSettings.EDITOR_TAB_LIMIT); + isModified |= isModified(myTabTitleLimitField, uiSettings.EDITOR_TAB_TITLE_LIMIT); int tabPlacement = ((Integer)myEditorTabPlacement.getSelectedItem()).intValue(); isModified |= tabPlacement != uiSettings.EDITOR_TAB_PLACEMENT; isModified |= myHideKnownExtensions.isSelected() != uiSettings.HIDE_KNOWN_EXTENSION_IN_TABS; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/FormatChangedTextUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/FormatChangedTextUtil.java index bf77a3022a0e..af6bbdc931ca 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/FormatChangedTextUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/FormatChangedTextUtil.java @@ -289,7 +289,16 @@ public class FormatChangedTextUtil { } try { - List<Range> changedRanges = new RangesBuilder(document, documentFromVcs).getRanges(); + List<Range> changedRanges; + + LineStatusTracker tracker = LineStatusTrackerManager.getInstance(project).getLineStatusTracker(document); + if (tracker != null) { + changedRanges = tracker.getRanges(); + } + else { + changedRanges = new RangesBuilder(document, documentFromVcs).getRanges(); + } + return getChangedTextRanges(document, changedRanges); } catch (FilesTooBigForDiffException e) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeAction.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeAction.java index 4966a9466a26..8ec833e8258b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeAction.java @@ -79,9 +79,6 @@ public class ReformatCodeAction extends AnAction implements DumbAware { PsiDocumentManager.getInstance(project).commitAllDocuments(); final Editor editor = CommonDataKeys.EDITOR.getData(dataContext); final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext); - if (files == null) { - return; - } PsiFile file = null; final PsiDirectory dir; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index e0a3be5f423d..fcb080dcd277 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -71,6 +71,8 @@ import java.util.concurrent.atomic.AtomicReference; public class CodeCompletionHandlerBase { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CodeCompletionHandlerBase"); + private static final Key<Boolean> CARET_PROCESSED = Key.create("CodeCompletionHandlerBase.caretProcessed"); + @NotNull private final CompletionType myCompletionType; final boolean invokedExplicitly; final boolean synchronous; @@ -108,6 +110,13 @@ public class CodeCompletionHandlerBase { } public final void invokeCompletion(@NotNull final Project project, @NotNull final Editor editor, int time, boolean hasModifiers, boolean restarted) { + clearCaretMarkers(editor); + invokeCompletion(project, editor, time, hasModifiers, restarted, editor.getCaretModel().getPrimaryCaret()); + } + + public final void invokeCompletion(@NotNull final Project project, @NotNull final Editor editor, int time, boolean hasModifiers, boolean restarted, @NotNull final Caret caret) { + markCaretAsProcessed(caret); + if (invokedExplicitly) { CompletionLookupArranger.applyLastCompletionStatisticsUpdate(); } @@ -162,12 +171,12 @@ public class CodeCompletionHandlerBase { PsiDocumentManager.getInstance(project).commitAllDocuments(); CompletionAssertions.checkEditorValid(editor); - final PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project); + final PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(caret, project); assert psiFile != null : "no PSI file: " + FileDocumentManager.getInstance().getFile(editor.getDocument()); psiFile.putUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING, Boolean.TRUE); CompletionAssertions.assertCommitSuccessful(editor, psiFile); - initializationContext[0] = runContributorsBeforeCompletion(editor, psiFile, invocationCount); + initializationContext[0] = runContributorsBeforeCompletion(editor, psiFile, invocationCount, caret); } }; ApplicationManager.getApplication().runWriteAction(runnable); @@ -186,9 +195,9 @@ public class CodeCompletionHandlerBase { insertDummyIdentifier(initializationContext[0], hasModifiers, invocationCount); } - private CompletionInitializationContext runContributorsBeforeCompletion(Editor editor, PsiFile psiFile, int invocationCount) { + private CompletionInitializationContext runContributorsBeforeCompletion(Editor editor, PsiFile psiFile, int invocationCount, Caret caret) { final Ref<CompletionContributor> current = Ref.create(null); - CompletionInitializationContext context = new CompletionInitializationContext(editor, psiFile, myCompletionType, invocationCount) { + CompletionInitializationContext context = new CompletionInitializationContext(editor, caret, psiFile, myCompletionType, invocationCount) { CompletionContributor dummyIdentifierChanger; @Override @@ -400,8 +409,15 @@ public class CodeCompletionHandlerBase { final LookupElement[] items, boolean hasModifiers) { if (items.length == 0) { LookupManager.getInstance(indicator.getProject()).hideActiveLookup(); - indicator.handleEmptyLookup(true); - checkNotSync(indicator, items); + + Caret nextCaret = getNextCaretToProcess(indicator.getEditor()); + if (nextCaret != null) { + invokeCompletion(indicator.getProject(), indicator.getEditor(), indicator.getParameters().getInvocationCount(), hasModifiers, false, nextCaret); + } + else { + indicator.handleEmptyLookup(true); + checkNotSync(indicator, items); + } return; } @@ -866,4 +882,23 @@ public class CodeCompletionHandlerBase { } }; } + + private static void clearCaretMarkers(@NotNull Editor editor) { + for (Caret caret : editor.getCaretModel().getAllCarets()) { + caret.putUserData(CARET_PROCESSED, null); + } + } + + private static void markCaretAsProcessed(@NotNull Caret caret) { + caret.putUserData(CARET_PROCESSED, Boolean.TRUE); + } + + private static Caret getNextCaretToProcess(@NotNull Editor editor) { + for (Caret caret : editor.getCaretModel().getAllCarets()) { + if (caret.getUserData(CARET_PROCESSED) == null) { + return caret; + } + } + return null; + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/InjectedGeneralHighlightingPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/InjectedGeneralHighlightingPass.java index 8560129194f1..0321191294c5 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/InjectedGeneralHighlightingPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/InjectedGeneralHighlightingPass.java @@ -73,7 +73,7 @@ public class InjectedGeneralHighlightingPass extends GeneralHighlightingPass imp @Override protected void collectInformationWithProgress(@NotNull final ProgressIndicator progress) { - if (!Registry.is("editor.injected.highlighting.enabled", true)) return; + if (!Registry.is("editor.injected.highlighting.enabled")) return; final Set<HighlightInfo> gotHighlights = new THashSet<HighlightInfo>(100); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WolfHighlightingPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WolfHighlightingPass.java index 9211bead784b..6ff242108eed 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WolfHighlightingPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WolfHighlightingPass.java @@ -38,7 +38,7 @@ class WolfHighlightingPass extends ProgressableTextEditorHighlightingPass implem @Override protected void collectInformationWithProgress(@NotNull final ProgressIndicator progress) { - if (!Registry.is("wolf.the.problem.solver", true)) return; + if (!Registry.is("wolf.the.problem.solver")) return; final WolfTheProblemSolver solver = WolfTheProblemSolver.getInstance(myProject); if (solver instanceof WolfTheProblemSolverImpl) { ((WolfTheProblemSolverImpl)solver).startCheckingIfVincentSolvedProblemsYet(progress, this); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index c27ce400049e..05a1e01c6aa1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -261,10 +261,20 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp } public void showJavaDocInfo(final Editor editor, @Nullable final PsiFile file, boolean requestFocus) { - showJavaDocInfo(editor, file, requestFocus, true); + showJavaDocInfo(editor, file, requestFocus, null); } - private void showJavaDocInfo(final Editor editor, @Nullable final PsiFile file, boolean requestFocus, final boolean autoupdate) { + public void showJavaDocInfo(final Editor editor, + @Nullable final PsiFile file, + boolean requestFocus, + final Runnable closeCallback) { + showJavaDocInfo(editor, file, requestFocus, true, closeCallback); + } + + private void showJavaDocInfo(final Editor editor, + @Nullable final PsiFile file, + boolean requestFocus, + final boolean autoupdate, @Nullable final Runnable closeCallback) { myEditor = editor; final Project project = getProject(file); PsiDocumentManager.getInstance(project).commitAllDocuments(); @@ -314,7 +324,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp return; } if (lookupIteObject instanceof PsiElement) { - doShowJavaDocInfo((PsiElement)lookupIteObject, false, this, originalElement, autoupdate); + doShowJavaDocInfo((PsiElement)lookupIteObject, false, this, originalElement, autoupdate, closeCallback); return; } @@ -337,12 +347,12 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp } } else { - doShowJavaDocInfo(element, false, this, originalElement, autoupdate); + doShowJavaDocInfo(element, false, this, originalElement, autoupdate, closeCallback); } } }; - doShowJavaDocInfo(element, requestFocus, updateProcessor, originalElement, autoupdate); + doShowJavaDocInfo(element, requestFocus, updateProcessor, originalElement, autoupdate, closeCallback); } public PsiElement findTargetElement(Editor editor, PsiFile file) { @@ -969,7 +979,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp @Override protected void doUpdateComponent(Editor editor, PsiFile psiFile) { - showJavaDocInfo(editor, psiFile, false, true); + showJavaDocInfo(editor, psiFile, false, true, null); } @Override diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CopyHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CopyHandler.java index 2a46c08fc2dd..b738bafb038a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CopyHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CopyHandler.java @@ -19,12 +19,12 @@ package com.intellij.codeInsight.editorActions; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; import com.intellij.openapi.editor.actions.CopyAction; import com.intellij.openapi.editor.actions.EditorActionUtil; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.impl.EditorCopyPasteHelperImpl; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.project.Project; @@ -92,7 +92,7 @@ public class CopyHandler extends EditorActionHandler { } String text = editor.getCaretModel().supportsMultipleCarets() - ? CopyPasteSupport.getSelectedTextForClipboard(editor, transferableDatas) + ? EditorCopyPasteHelperImpl.getSelectedTextForClipboard(editor, transferableDatas) : selectionModel.getSelectedText(); String rawText = TextBlockTransferable.convertLineSeparators(text, "\n", transferableDatas); String escapedText = null; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java index 345cb80bae69..17916e008501 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java @@ -17,15 +17,22 @@ package com.intellij.codeInsight.editorActions; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeStyle.CodeStyleFacade; +import com.intellij.formatting.*; +import com.intellij.lang.LanguageFormatting; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.CaretModel; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; @@ -36,40 +43,75 @@ import org.jetbrains.annotations.NotNull; public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { private static final Logger LOG = Logger.getInstance(IndentingBackspaceHandler.class); + private boolean isApplicable; private boolean caretWasAtLineStart; + private String precalculatedSpacing; @Override public void beforeCharDeleted(char c, PsiFile file, Editor editor) { - caretWasAtLineStart = editor.getCaretModel().getLogicalPosition().column == 0; - } - - @Override - public boolean charDeleted(char c, PsiFile file, Editor editor) { if (CodeInsightSettings.getInstance().SMART_BACKSPACE != CodeInsightSettings.AUTOINDENT || !StringUtil.isWhiteSpace(c)) { - return false; + isApplicable = false; + return; } LanguageCodeStyleSettingsProvider codeStyleSettingsProvider = LanguageCodeStyleSettingsProvider.forLanguage(file.getLanguage()); if (codeStyleSettingsProvider != null && codeStyleSettingsProvider.isIndentBasedLanguageSemantics()) { + isApplicable = false; + return; + } + Document document = editor.getDocument(); + CharSequence charSequence = document.getCharsSequence(); + CaretModel caretModel = editor.getCaretModel(); + int caretOffset = caretModel.getOffset(); + LogicalPosition pos = caretModel.getLogicalPosition(); + isApplicable = true; + caretWasAtLineStart = pos.column == 0; + precalculatedSpacing = null; + if (caretWasAtLineStart && pos.line > 0 && caretOffset < charSequence.length() && !StringUtil.isWhiteSpace(charSequence.charAt(caretOffset))) { + int prevLineEnd = document.getLineEndOffset(pos.line - 1); + if (prevLineEnd > 0 && !StringUtil.isWhiteSpace(charSequence.charAt(prevLineEnd - 1))) { + PsiDocumentManager.getInstance(file.getProject()).commitDocument(document); + precalculatedSpacing = getSpacing(file, caretOffset); + } + } + } + + @Override + public boolean charDeleted(char c, PsiFile file, Editor editor) { + if (!isApplicable) { return false; } + Project project = file.getProject(); Document document = editor.getDocument(); + CaretModel caretModel = editor.getCaretModel(); - int caretOffset = editor.getCaretModel().getOffset(); + int caretOffset = caretModel.getOffset(); int offset = CharArrayUtil.shiftForward(document.getCharsSequence(), caretOffset, " \t"); int beforeWhitespaceOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), offset - 1, " \t") + 1; - LogicalPosition logicalPosition = caretOffset < offset ? editor.offsetToLogicalPosition(offset) : editor.getCaretModel().getLogicalPosition(); + LogicalPosition logicalPosition = caretOffset < offset ? editor.offsetToLogicalPosition(offset) : caretModel.getLogicalPosition(); int lineStartOffset = document.getLineStartOffset(logicalPosition.line); if (lineStartOffset < beforeWhitespaceOffset) { - if (caretWasAtLineStart && beforeWhitespaceOffset < offset) { - document.deleteString(beforeWhitespaceOffset, offset); - return true; + if (caretWasAtLineStart && beforeWhitespaceOffset <= offset) { + String spacing; + if (precalculatedSpacing == null) { + PsiDocumentManager.getInstance(project).commitDocument(document); + spacing = getSpacing(file, offset); + } + else { + spacing = precalculatedSpacing; + } + if (beforeWhitespaceOffset < offset || !spacing.isEmpty()) { + document.replaceString(beforeWhitespaceOffset, offset, spacing); + caretModel.moveToOffset(beforeWhitespaceOffset + spacing.length()); + return true; + } } return false; } - CodeStyleFacade codeStyleFacade = CodeStyleFacade.getInstance(editor.getProject()); - String indent = codeStyleFacade.getLineIndent(document, lineStartOffset); + PsiDocumentManager.getInstance(project).commitDocument(document); + CodeStyleFacade codeStyleFacade = CodeStyleFacade.getInstance(project); + String indent = codeStyleFacade.getLineIndent(document, offset); if (indent == null) { return false; } @@ -79,7 +121,7 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { if (logicalPosition.column == targetColumn) { if (caretOffset < offset) { - editor.getCaretModel().moveToLogicalPosition(logicalPosition); + caretModel.moveToLogicalPosition(logicalPosition); return true; } return false; @@ -87,7 +129,7 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { if (caretWasAtLineStart || logicalPosition.column > targetColumn) { document.replaceString(lineStartOffset, offset, indent); - editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(logicalPosition.line, targetColumn)); + caretModel.moveToLogicalPosition(new LogicalPosition(logicalPosition.line, targetColumn)); return true; } @@ -100,12 +142,13 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { int targetOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), prevLineEndOffset - 1, " \t") + 1; if (prevLineStartOffset < targetOffset) { - document.deleteString(targetOffset, offset); - editor.getCaretModel().moveToOffset(targetOffset); + String spacing = getSpacing(file, offset); + document.replaceString(targetOffset, offset, spacing); + caretModel.moveToOffset(targetOffset + spacing.length()); } else { document.replaceString(prevLineStartOffset, offset, indent); - editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(logicalPosition.line - 1, targetColumn)); + caretModel.moveToLogicalPosition(new LogicalPosition(logicalPosition.line - 1, targetColumn)); } return true; } @@ -132,4 +175,15 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { } return width; } + + private static String getSpacing(PsiFile file, int offset) { + FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file); + if (builder == null) { + return ""; + } + CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(file.getProject()); + FormattingModel model = builder.createModel(file, settings); + int spacing = FormatterEx.getInstance().getSpacingForBlockAtOffset(model, offset); + return StringUtil.repeatSymbol(' ', spacing); + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java index bb21eac69ecd..ba4768f73099 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java @@ -148,14 +148,17 @@ public class TypedHandler extends TypedActionHandlerBase { return; } + final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project); + final Document originalDocument = originalEditor.getDocument(); originalEditor.getCaretModel().runForEachCaret(new CaretAction() { @Override public void perform(Caret caret) { - PsiDocumentManager.getInstance(project) - .doPostponedOperationsAndUnblockDocument(originalEditor.getDocument()); // to clean up after previous caret processing + if (psiDocumentManager.isDocumentBlockedByPsi(originalDocument)) { + psiDocumentManager.doPostponedOperationsAndUnblockDocument(originalDocument); // to clean up after previous caret processing + } Editor editor = injectedEditorIfCharTypedIsSignificant(charTyped, originalEditor, originalFile); - PsiFile file = editor == originalEditor ? originalFile : PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); + PsiFile file = editor == originalEditor ? originalFile : psiDocumentManager.getPsiFile(editor.getDocument()); final TypedHandlerDelegate[] delegates = Extensions.getExtensions(TypedHandlerDelegate.EP_NAME); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index 3a1f04b80414..9a71909fcca8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -372,31 +372,34 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable, checkValid(); CollectionListModel<LookupElement> listModel = getListModel(); - synchronized (myList) { - Pair<List<LookupElement>, Integer> pair = myPresentableArranger.arrangeItems(this, onExplicitAction || reused); - List<LookupElement> items = pair.first; - Integer toSelect = pair.second; - if (toSelect == null || toSelect < 0 || items.size() > 0 && toSelect >= items.size()) { - LOG.error("Arranger " + myPresentableArranger + " returned invalid selection index=" + toSelect + "; items=" + items); - } - - myOffsets.checkMinPrefixLengthChanges(items, this); - List<LookupElement> oldModel = listModel.toList(); - listModel.removeAll(); - if (!items.isEmpty()) { - listModel.add(items); - } - else { - addEmptyItem(listModel); - } + Pair<List<LookupElement>, Integer> pair; + synchronized (myList) { + pair = myPresentableArranger.arrangeItems(this, onExplicitAction || reused); + } + + List<LookupElement> items = pair.first; + Integer toSelect = pair.second; + if (toSelect == null || toSelect < 0 || items.size() > 0 && toSelect >= items.size()) { + LOG.error("Arranger " + myPresentableArranger + " returned invalid selection index=" + toSelect + "; items=" + items); + toSelect = 0; + } - updateListHeight(listModel); + myOffsets.checkMinPrefixLengthChanges(items, this); + List<LookupElement> oldModel = listModel.toList(); - myList.setSelectedIndex(toSelect); - return !ContainerUtil.equalsIdentity(oldModel, items); + listModel.removeAll(); + if (!items.isEmpty()) { + listModel.add(items); } + else { + addEmptyItem(listModel); + } + + updateListHeight(listModel); + myList.setSelectedIndex(toSelect); + return !ContainerUtil.equalsIdentity(oldModel, items); } private boolean isSelectionVisible() { @@ -566,7 +569,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable, public void perform(Caret caret) { EditorModificationUtil.deleteSelectedText(hostEditor); final int caretOffset = hostEditor.getCaretModel().getOffset(); - int lookupStart = caretOffset - prefix; + int lookupStart = Math.max(caretOffset - prefix, 0); int len = hostEditor.getDocument().getTextLength(); LOG.assertTrue(lookupStart >= 0 && lookupStart <= len, diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java index 3c0e84a11656..3617c944e888 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java @@ -19,30 +19,46 @@ package com.intellij.codeInsight.navigation; import com.intellij.ide.util.DefaultPsiElementCellRenderer; import com.intellij.ide.util.EditSourceUtil; import com.intellij.ide.util.PsiElementListCellRenderer; +import com.intellij.navigation.GotoRelatedItem; +import com.intellij.navigation.GotoRelatedProvider; import com.intellij.navigation.NavigationItem; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.markup.HighlighterTargetArea; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.fileEditor.impl.EditorHistoryManager; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.PopupChooserBuilder; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.Navigatable; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.ElementBase; import com.intellij.psi.search.PsiElementProcessor; -import com.intellij.ui.JBListWithHintProvider; +import com.intellij.ui.*; +import com.intellij.ui.popup.list.ListPopupImpl; +import com.intellij.ui.popup.list.PopupListElementRenderer; +import com.intellij.util.Processor; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.*; +import java.util.List; /** * @author ven @@ -220,4 +236,242 @@ public final class NavigationUtil { } return attributes; } + + @NotNull + public static JBPopup getRelatedItemsPopup(final List<? extends GotoRelatedItem> items, String title) { + Object[] elements = new Object[items.size()]; + //todo[nik] move presentation logic to GotoRelatedItem class + final Map<PsiElement, GotoRelatedItem> itemsMap = new HashMap<PsiElement, GotoRelatedItem>(); + for (int i = 0; i < items.size(); i++) { + GotoRelatedItem item = items.get(i); + elements[i] = item.getElement() != null ? item.getElement() : item; + itemsMap.put(item.getElement(), item); + } + + return getPsiElementPopup(elements, itemsMap, title, new Processor<Object>() { + @Override + public boolean process(Object element) { + if (element instanceof PsiElement) { + //noinspection SuspiciousMethodCalls + itemsMap.get(element).navigate(); + } + else { + ((GotoRelatedItem)element).navigate(); + } + return true; + } + } + ); + } + + private static JBPopup getPsiElementPopup(final Object[] elements, final Map<PsiElement, GotoRelatedItem> itemsMap, + final String title, final Processor<Object> processor) { + + final Ref<Boolean> hasMnemonic = Ref.create(false); + final DefaultPsiElementCellRenderer renderer = new DefaultPsiElementCellRenderer() { + { + setFocusBorderEnabled(false); + } + + @Override + public String getElementText(PsiElement element) { + String customName = itemsMap.get(element).getCustomName(); + return (customName != null ? customName : super.getElementText(element)); + } + + @Override + protected Icon getIcon(PsiElement element) { + Icon customIcon = itemsMap.get(element).getCustomIcon(); + return customIcon != null ? customIcon : super.getIcon(element); + } + + @Override + public String getContainerText(PsiElement element, String name) { + String customContainerName = itemsMap.get(element).getCustomContainerName(); + + if (customContainerName != null) { + return customContainerName; + } + PsiFile file = element.getContainingFile(); + return file != null && !getElementText(element).equals(file.getName()) + ? "(" + file.getName() + ")" + : null; + } + + @Override + protected DefaultListCellRenderer getRightCellRenderer(Object value) { + return null; + } + + @Override + protected boolean customizeNonPsiElementLeftRenderer(ColoredListCellRenderer renderer, + JList list, + Object value, + int index, + boolean selected, + boolean hasFocus) { + final GotoRelatedItem item = (GotoRelatedItem)value; + Color color = list.getForeground(); + final SimpleTextAttributes nameAttributes = new SimpleTextAttributes(Font.PLAIN, color); + final String name = item.getCustomName(); + if (name == null) return false; + renderer.append(name, nameAttributes); + renderer.setIcon(item.getCustomIcon()); + return true; + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + final JPanel component = (JPanel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (!hasMnemonic.get()) return component; + + final JPanel panelWithMnemonic = new JPanel(new BorderLayout()); + final int mnemonic = getMnemonic(value, itemsMap); + final JLabel label = new JLabel(""); + if (mnemonic != -1) { + label.setText(mnemonic + "."); + label.setDisplayedMnemonicIndex(0); + } + label.setPreferredSize(new JLabel("8.").getPreferredSize()); + + final JComponent leftRenderer = (JComponent)component.getComponents()[0]; + component.remove(leftRenderer); + panelWithMnemonic.setBorder(BorderFactory.createEmptyBorder(0, 7, 0, 0)); + panelWithMnemonic.setBackground(leftRenderer.getBackground()); + label.setBackground(leftRenderer.getBackground()); + panelWithMnemonic.add(label, BorderLayout.WEST); + panelWithMnemonic.add(leftRenderer, BorderLayout.CENTER); + component.add(panelWithMnemonic); + return component; + } + }; + final ListPopupImpl popup = new ListPopupImpl(new BaseListPopupStep<Object>(title, Arrays.asList(elements)) { + @Override + public boolean isSpeedSearchEnabled() { + return true; + } + + @Override + public String getIndexedString(Object value) { + if (value instanceof GotoRelatedItem) { + //noinspection ConstantConditions + return ((GotoRelatedItem)value).getCustomName(); + } + final PsiElement element = (PsiElement)value; + return renderer.getElementText(element) + " " + renderer.getContainerText(element, null); + } + + @Override + public PopupStep onChosen(Object selectedValue, boolean finalChoice) { + processor.process(selectedValue); + return super.onChosen(selectedValue, finalChoice); + } + }) { + }; + popup.getList().setCellRenderer(new PopupListElementRenderer(popup) { + Map<Object, String> separators = new HashMap<Object, String>(); + { + final ListModel model = popup.getList().getModel(); + String current = null; + boolean hasTitle = false; + for (int i = 0; i < model.getSize(); i++) { + final Object element = model.getElementAt(i); + final GotoRelatedItem item = itemsMap.get(element); + if (item != null && !StringUtil.equals(current, item.getGroup())) { + current = item.getGroup(); + separators.put(element, current); + if (!hasTitle && !StringUtil.isEmpty(current)) { + hasTitle = true; + } + } + } + + if (!hasTitle) { + separators.remove(model.getElementAt(0)); + } + } + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + final Component component = renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + final String separator = separators.get(value); + + if (separator != null) { + JPanel panel = new JPanel(new BorderLayout()); + panel.add(component, BorderLayout.CENTER); + final SeparatorWithText sep = new SeparatorWithText() { + @Override + protected void paintComponent(Graphics g) { + g.setColor(new JBColor(Color.WHITE, UIUtil.getSeparatorColor())); + g.fillRect(0,0,getWidth(), getHeight()); + super.paintComponent(g); + } + }; + sep.setCaption(separator); + panel.add(sep, BorderLayout.NORTH); + return panel; + } + return component; + } + }); + + popup.setMinimumSize(new Dimension(200, -1)); + + for (Object item : elements) { + final int mnemonic = getMnemonic(item, itemsMap); + if (mnemonic != -1) { + final Action action = createNumberAction(mnemonic, popup, itemsMap, processor); + popup.registerAction(mnemonic + "Action", KeyStroke.getKeyStroke(String.valueOf(mnemonic)), action); + popup.registerAction(mnemonic + "Action", KeyStroke.getKeyStroke("NUMPAD" + String.valueOf(mnemonic)), action); + hasMnemonic.set(true); + } + } + return popup; + } + + private static Action createNumberAction(final int mnemonic, + final ListPopupImpl listPopup, + final Map<PsiElement, GotoRelatedItem> itemsMap, + final Processor<Object> processor) { + return new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + for (final Object item : listPopup.getListStep().getValues()) { + if (getMnemonic(item, itemsMap) == mnemonic) { + listPopup.setFinalRunnable(new Runnable() { + @Override + public void run() { + processor.process(item); + } + }); + listPopup.closeOk(null); + } + } + } + }; + } + + private static int getMnemonic(Object item, Map<PsiElement, GotoRelatedItem> itemsMap) { + return (item instanceof GotoRelatedItem ? (GotoRelatedItem)item : itemsMap.get((PsiElement)item)).getMnemonic(); + } + + @NotNull + public static List<GotoRelatedItem> collectRelatedItems(@NotNull PsiElement contextElement, @Nullable DataContext dataContext) { + Set<GotoRelatedItem> items = ContainerUtil.newLinkedHashSet(); + for (GotoRelatedProvider provider : Extensions.getExtensions(GotoRelatedProvider.EP_NAME)) { + items.addAll(provider.getItems(contextElement)); + if (dataContext != null) { + items.addAll(provider.getItems(dataContext)); + } + } + GotoRelatedItem[] result = items.toArray(new GotoRelatedItem[items.size()]); + Arrays.sort(result, new Comparator<GotoRelatedItem>() { + @Override + public int compare(GotoRelatedItem i1, GotoRelatedItem i2) { + String o1 = i1.getGroup(); + String o2 = i2.getGroup(); + return StringUtil.isEmpty(o1) ? 1 : StringUtil.isEmpty(o2) ? -1 : o1.compareTo(o2); + } + }); + return Arrays.asList(result); + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/problems/MockWolfTheProblemSolver.java b/platform/lang-impl/src/com/intellij/codeInsight/problems/MockWolfTheProblemSolver.java index eeb5e04b8aab..4ea365f34b0c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/problems/MockWolfTheProblemSolver.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/problems/MockWolfTheProblemSolver.java @@ -44,6 +44,11 @@ public class MockWolfTheProblemSolver extends WolfTheProblemSolver { } @Override + public void weHaveGotNonIgnorableProblems(@NotNull VirtualFile virtualFile, @NotNull List<Problem> problems) { + if (myDelegate != null) myDelegate.weHaveGotNonIgnorableProblems(virtualFile, problems); + } + + @Override public boolean hasProblemFilesBeneath(@NotNull final Condition<VirtualFile> condition) { return false; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/problems/WolfTheProblemSolverImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/problems/WolfTheProblemSolverImpl.java index 45b4532713c4..fab185e6ddc0 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/problems/WolfTheProblemSolverImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/problems/WolfTheProblemSolverImpl.java @@ -413,6 +413,12 @@ public class WolfTheProblemSolverImpl extends WolfTheProblemSolver { public void weHaveGotProblems(@NotNull final VirtualFile virtualFile, @NotNull List<Problem> problems) { if (problems.isEmpty()) return; if (!isToBeHighlighted(virtualFile)) return; + weHaveGotNonIgnorableProblems(virtualFile, problems); + } + + @Override + public void weHaveGotNonIgnorableProblems(@NotNull VirtualFile virtualFile, @NotNull List<Problem> problems) { + if (problems.isEmpty()) return; boolean fireListener = false; synchronized (myProblems) { ProblemFileInfo storedProblems = myProblems.get(virtualFile); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java index 516ed9118c8c..0c838cd05ec5 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java @@ -212,8 +212,11 @@ public class TemplateState implements Disposable { if (variableName.equals(TemplateImpl.END)) { return new TextResult(""); } - if (myPredefinedVariableValues != null && myPredefinedVariableValues.containsKey(variableName)) { - return new TextResult(myPredefinedVariableValues.get(variableName)); + if (myPredefinedVariableValues != null) { + String text = myPredefinedVariableValues.get(variableName); + if (text != null) { + return new TextResult(text); + } } CharSequence text = myDocument.getCharsSequence(); int segmentNumber = myTemplate.getVariableSegmentNumber(variableName); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ChooserExpressionSelector.java index f6b0cedae31e..2989f0d0c97c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ChooserExpressionSelector.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.template.postfix.templates; + import com.intellij.codeInsight.unwrap.ScopeHighlighter; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; @@ -30,45 +31,43 @@ import org.jetbrains.annotations.NotNull; import java.util.List; /** - * @author ignatov + * See {@link PostfixTemplateExpressionSelector} for description */ -public abstract class ExpressionPostfixTemplateWithChooser extends PostfixTemplate { +public class ChooserExpressionSelector implements PostfixTemplateExpressionSelector { @NotNull - protected final PostfixTemplatePsiInfoBase myInfo; + private final Condition<PsiElement> myCondition; - protected ExpressionPostfixTemplateWithChooser(@NotNull String name, @NotNull String example, @NotNull PostfixTemplatePsiInfoBase info) { - super(name, example); - myInfo = info; + public ChooserExpressionSelector(@NotNull Condition<PsiElement> condition) { + myCondition = condition; } - protected ExpressionPostfixTemplateWithChooser(@NotNull String name, - @NotNull String key, - @NotNull String example, - @NotNull PostfixTemplatePsiInfoBase info) { - super(name, key, example); - myInfo = info; - } - @Override - public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - return !getExpressions(context, copyDocument, newOffset).isEmpty(); + public boolean hasExpression(@NotNull final PostfixTemplateWithExpressionSelector postfixTemplate, + @NotNull PsiElement context, + @NotNull Document copyDocument, + int newOffset) { + return !getExpressions(postfixTemplate, context, copyDocument, newOffset).isEmpty(); } - @Override - public void expand(@NotNull PsiElement context, @NotNull final Editor editor) { - List<PsiElement> expressions = getExpressions(context, editor.getDocument(), editor.getCaretModel().getOffset()); + public void expandTemplate(@NotNull final PostfixTemplateWithExpressionSelector postfixTemplate, + @NotNull PsiElement context, + @NotNull final Editor editor) { + List<PsiElement> expressions = + getExpressions(postfixTemplate, context, editor.getDocument(), editor.getCaretModel().getOffset()); if (expressions.isEmpty()) { PostfixTemplatesUtils.showErrorHint(context.getProject(), editor); } else if (expressions.size() == 1) { - doIt(editor, expressions.get(0)); + postfixTemplate.expandForChooseExpression(expressions.get(0), editor); } else { if (ApplicationManager.getApplication().isUnitTestMode()) { - doIt(editor, expressions.get(expressions.size() - 1)); + PsiElement item = ContainerUtil.getLastItem(expressions); + assert item != null; + postfixTemplate.expandForChooseExpression(item, editor); return; } @@ -81,22 +80,25 @@ public abstract class ExpressionPostfixTemplateWithChooser extends PostfixTempla public void run() { CommandProcessor.getInstance().executeCommand(e.getProject(), new Runnable() { public void run() { - doIt(editor, e); + postfixTemplate.expandForChooseExpression(e, editor); } }, "Expand postfix template", PostfixLiveTemplate.POSTFIX_TEMPLATE_ID); } }); } }, - myInfo.getRenderer(), + postfixTemplate.getPsiInfo().getRenderer(), "Expressions", 0, ScopeHighlighter.NATURAL_RANGER ); } } @NotNull - protected List<PsiElement> getExpressions(@NotNull PsiElement context, @NotNull Document document, final int offset) { - List<PsiElement> possibleExpressions = myInfo.getExpressions(context, document, offset); + private List<PsiElement> getExpressions(@NotNull PostfixTemplateWithExpressionSelector postfixTemplate, + @NotNull PsiElement context, + @NotNull Document document, + final int offset) { + List<PsiElement> possibleExpressions = postfixTemplate.getPsiInfo().getExpressions(context, document, offset); List<PsiElement> expressions = ContainerUtil.filter(possibleExpressions, new Condition<PsiElement>() { @Override @@ -105,19 +107,13 @@ public abstract class ExpressionPostfixTemplateWithChooser extends PostfixTempla } } ); - return ContainerUtil.filter(expressions.isEmpty() ? maybeTopmostExpression(context) : expressions, getTypeCondition()); + return ContainerUtil + .filter(expressions.isEmpty() ? maybeTopmostExpression(postfixTemplate, context) : expressions, myCondition); } - @NotNull - @SuppressWarnings("unchecked") - protected Condition<PsiElement> getTypeCondition() { - return Condition.TRUE; - } @NotNull - private List<PsiElement> maybeTopmostExpression(@NotNull PsiElement context) { - return ContainerUtil.createMaybeSingletonList(myInfo.getTopmostExpression(context)); + private static List<PsiElement> maybeTopmostExpression(@NotNull PostfixTemplateWithExpressionSelector postfixTemplate, @NotNull PsiElement context) { + return ContainerUtil.createMaybeSingletonList(postfixTemplate.getPsiInfo().getTopmostExpression(context)); } - - protected abstract void doIt(@NotNull Editor editor, @NotNull PsiElement expression); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/NotPostfixTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/NotPostfixTemplate.java index 9c1d9c66b94d..3944cd930af0 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/NotPostfixTemplate.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/NotPostfixTemplate.java @@ -16,26 +16,34 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; -public class NotPostfixTemplate extends ExpressionPostfixTemplateWithChooser { +import static com.intellij.codeInsight.template.postfix.templates.PostfixTemplatesUtils.selectorWithChooser; - public NotPostfixTemplate(@NotNull PostfixTemplatePsiInfoBase info) { - super("not", "!expr", info); +public class NotPostfixTemplate extends PostfixTemplateWithExpressionSelector { + + public NotPostfixTemplate(@NotNull PostfixTemplatePsiInfo info, @NotNull Condition<PsiElement> typeChecker) { + super("not", "!expr", info, selectorWithChooser(typeChecker)); } + public NotPostfixTemplate(@NotNull PostfixTemplatePsiInfo info) { + super("not", "!expr", info, selectorWithChooser()); + } public NotPostfixTemplate(@NotNull String name, @NotNull String key, @NotNull String example, - @NotNull PostfixTemplatePsiInfoBase info) { - super(name, key, example, info); + @NotNull PostfixTemplatePsiInfo info, + @NotNull Condition<PsiElement> typeChecker + ) { + super(name, key, example, info, selectorWithChooser(typeChecker)); } @Override - protected void doIt(@NotNull Editor editor, @NotNull PsiElement expression) { - PsiElement element = myInfo.getNegatedExpression(expression); + protected void expandForChooseExpression(@NotNull PsiElement expression, @NotNull Editor editor) { + PsiElement element = myPsiInfo.getNegatedExpression(expression); expression.replace(element); } }
\ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ParenthesizedPostfixTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ParenthesizedPostfixTemplate.java index 9a5d03a9359c..3f1dd1143348 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ParenthesizedPostfixTemplate.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ParenthesizedPostfixTemplate.java @@ -16,16 +16,25 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; -public class ParenthesizedPostfixTemplate extends ExpressionPostfixTemplateWithChooser { - public ParenthesizedPostfixTemplate(PostfixTemplatePsiInfoBase psiInfo) { - super("par", "(expr)", psiInfo); +import static com.intellij.codeInsight.template.postfix.templates.PostfixTemplatesUtils.selectorWithChooser; + +public class ParenthesizedPostfixTemplate extends PostfixTemplateWithExpressionSelector { + + public ParenthesizedPostfixTemplate(PostfixTemplatePsiInfo psiInfo, Condition<PsiElement> condition) { + super("par", "(expr)", psiInfo, selectorWithChooser(condition)); + } + + + public ParenthesizedPostfixTemplate(PostfixTemplatePsiInfo psiInfo) { + super("par", "(expr)", psiInfo, selectorWithChooser()); } @Override - protected void doIt(@NotNull Editor editor, @NotNull PsiElement expression) { - expression.replace(myInfo.createExpression(expression, "(", ")")); + protected void expandForChooseExpression(@NotNull PsiElement expression, @NotNull Editor editor) { + expression.replace(myPsiInfo.createExpression(expression, "(", ")")); } }
\ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateExpressionSelector.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateExpressionSelector.java new file mode 100644 index 000000000000..6b5d559da1de --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateExpressionSelector.java @@ -0,0 +1,54 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.postfix.templates; + + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +/** + * Interface provides method used in {@link com.intellij.codeInsight.template.postfix.templates.PostfixTemplateWithExpressionSelector} + * + * You should implement the interface if you have non-trivial logic how to determine expression for next processing in postfix template + * Otherwise, you can use one of existing simple implementations: + * + * 1) {@link com.intellij.codeInsight.template.postfix.templates.ChooserExpressionSelector} - The selector get all expression + * in the current position and show to user chooser for these expressions. + * + * 2) {@link com.intellij.codeInsight.template.postfix.templates.TopmostExpressionSelector} - The selector pass to postfix template + * top most expression in the current position + * + * + */ +public interface PostfixTemplateExpressionSelector { + + /** + * Check that we can select not-null expression(PsiElement) in current context + */ + boolean hasExpression(@NotNull final PostfixTemplateWithExpressionSelector postfixTemplate, + @NotNull PsiElement context, + @NotNull Document copyDocument, + int newOffset); + + /** + * Select expression(PsiElement) and call postfixTemplate.expandForChooseExpression for selected expression + */ + void expandTemplate(@NotNull final PostfixTemplateWithExpressionSelector postfixTemplate, + @NotNull PsiElement context, + @NotNull final Editor editor); +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatePsiInfo.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatePsiInfo.java index 8f4716787453..6664d6404d64 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatePsiInfo.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatePsiInfo.java @@ -16,25 +16,42 @@ package com.intellij.codeInsight.template.postfix.templates; +import com.intellij.openapi.editor.Document; import com.intellij.psi.PsiElement; +import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public interface PostfixTemplatePsiInfo { +import java.util.List; + +public abstract class PostfixTemplatePsiInfo { @NotNull - PsiElement createStatement(@NotNull PsiElement context, - @NotNull String prefix, - @NotNull String suffix); + public abstract PsiElement createStatement(@NotNull PsiElement context, + @NotNull String prefix, + @NotNull String suffix); @NotNull - PsiElement createExpression(@NotNull PsiElement context, - @NotNull String prefix, - @NotNull String suffix); + public abstract PsiElement createExpression(@NotNull PsiElement context, + @NotNull String prefix, + @NotNull String suffix); @Nullable - PsiElement getTopmostExpression(@NotNull PsiElement element); + public abstract PsiElement getTopmostExpression(@NotNull PsiElement element); + + @NotNull + public abstract PsiElement getNegatedExpression(@NotNull PsiElement element); + + @NotNull + public abstract List<PsiElement> getExpressions(@NotNull PsiElement context, @NotNull Document document, int offset); @NotNull - PsiElement getNegatedExpression(@NotNull PsiElement element); + public Function<PsiElement, String> getRenderer() { + return new Function<PsiElement, String>() { + @Override + public String fun(@NotNull PsiElement element) { + return element.getText(); + } + }; + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatePsiInfoBase.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatePsiInfoBase.java deleted file mode 100644 index 7254e651ce41..000000000000 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatePsiInfoBase.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.codeInsight.template.postfix.templates; - -import com.intellij.openapi.editor.Document; -import com.intellij.psi.PsiElement; -import com.intellij.util.Function; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public abstract class PostfixTemplatePsiInfoBase implements PostfixTemplatePsiInfo { - - @NotNull - public abstract List<PsiElement> getExpressions(@NotNull PsiElement context, @NotNull Document document, int newOffset); - - @NotNull - public Function<PsiElement, String> getRenderer() { - return new Function<PsiElement, String>() { - @Override - public String fun(@NotNull PsiElement element) { - return element.getText(); - } - }; - } -} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateWithExpressionSelector.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateWithExpressionSelector.java new file mode 100644 index 000000000000..d7cfbafdf7a8 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateWithExpressionSelector.java @@ -0,0 +1,83 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.postfix.templates; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Condition; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.codeInsight.template.postfix.templates.PostfixTemplatesUtils.selectorTopmost; + +public abstract class PostfixTemplateWithExpressionSelector extends PostfixTemplate { + + @NotNull + protected final PostfixTemplatePsiInfo myPsiInfo; + @NotNull + private final PostfixTemplateExpressionSelector mySelector; + + protected PostfixTemplateWithExpressionSelector(@NotNull String name, + @NotNull String key, + @NotNull String example, + @NotNull PostfixTemplatePsiInfo psiInfo, + @NotNull PostfixTemplateExpressionSelector selector) { + super(name, key, example); + myPsiInfo = psiInfo; + mySelector = selector; + } + + + protected PostfixTemplateWithExpressionSelector(@NotNull String name, + @NotNull String example, + @NotNull PostfixTemplatePsiInfo psiInfo, + @NotNull PostfixTemplateExpressionSelector selector) { + super(name, example); + myPsiInfo = psiInfo; + mySelector = selector; + } + + protected PostfixTemplateWithExpressionSelector(@NotNull String name, + @NotNull String example, + @NotNull PostfixTemplatePsiInfo psiInfo, + @NotNull Condition<PsiElement> typeChecker) { + this(name, example, psiInfo, selectorTopmost(typeChecker)); + } + + protected PostfixTemplateWithExpressionSelector(@NotNull String name, + @NotNull String example, + @NotNull PostfixTemplatePsiInfo psiInfo) { + this(name, example, psiInfo, selectorTopmost()); + } + + + @Override + public final boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { + return mySelector.hasExpression(this, context, copyDocument, newOffset); + } + + @Override + public final void expand(@NotNull PsiElement context, @NotNull Editor editor) { + mySelector.expandTemplate(this, context, editor); + } + + protected abstract void expandForChooseExpression(@NotNull PsiElement expression, @NotNull Editor editor); + + @NotNull + PostfixTemplatePsiInfo getPsiInfo() { + return myPsiInfo; + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java index 877e10b53490..5ff744e3d72a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java @@ -21,6 +21,8 @@ import com.intellij.lang.surroundWith.Surrounder; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.refactoring.util.CommonRefactoringUtil; @@ -31,6 +33,22 @@ public abstract class PostfixTemplatesUtils { private PostfixTemplatesUtils() { } + public static PostfixTemplateExpressionSelector selectorWithChooser() { + return selectorWithChooser(Conditions.<PsiElement>alwaysTrue()); + } + + public static PostfixTemplateExpressionSelector selectorTopmost() { + return selectorTopmost(Conditions.<PsiElement>alwaysTrue()); + } + + public static PostfixTemplateExpressionSelector selectorWithChooser(Condition<PsiElement> condition) { + return new ChooserExpressionSelector(condition); + } + + public static PostfixTemplateExpressionSelector selectorTopmost(Condition<PsiElement> condition) { + return new TopmostExpressionSelector(condition); + } + @Nullable public static TextRange surround(@NotNull Surrounder surrounder, @NotNull Editor editor, diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementWrapPostfixTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementWrapPostfixTemplate.java index ed1605d3a9fb..5b5c3624f33b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementWrapPostfixTemplate.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementWrapPostfixTemplate.java @@ -22,7 +22,7 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; -public abstract class StatementWrapPostfixTemplate extends TypedPostfixTemplate { +public abstract class StatementWrapPostfixTemplate extends PostfixTemplateWithExpressionSelector { @SuppressWarnings("unchecked") protected StatementWrapPostfixTemplate(@NotNull String name, @@ -39,9 +39,7 @@ public abstract class StatementWrapPostfixTemplate extends TypedPostfixTemplate } @Override - public void expand(@NotNull PsiElement context, @NotNull Editor editor) { - PsiElement topmostExpression = myPsiInfo.getTopmostExpression(context); - assert topmostExpression != null; + public void expandForChooseExpression(@NotNull PsiElement topmostExpression, @NotNull Editor editor) { PsiElement parent = topmostExpression.getParent(); PsiElement expression = getWrappedExpression(topmostExpression); PsiElement replace = parent.replace(expression); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StringBasedPostfixTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StringBasedPostfixTemplate.java index 4d6582f071de..9f19b2011a49 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StringBasedPostfixTemplate.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StringBasedPostfixTemplate.java @@ -26,7 +26,7 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public abstract class StringBasedPostfixTemplate extends TypedPostfixTemplate { +public abstract class StringBasedPostfixTemplate extends PostfixTemplateWithExpressionSelector { public StringBasedPostfixTemplate(@NotNull String name, @NotNull String example, @@ -36,10 +36,8 @@ public abstract class StringBasedPostfixTemplate extends TypedPostfixTemplate { } @Override - public final void expand(@NotNull PsiElement context, @NotNull Editor editor) { - PsiElement expr = myPsiInfo.getTopmostExpression(context); - assert expr != null; - Project project = context.getProject(); + public final void expandForChooseExpression(@NotNull PsiElement expr, @NotNull Editor editor) { + Project project = expr.getProject(); Document document = editor.getDocument(); PsiElement elementForRemoving = shouldRemoveParent() ? expr.getParent() : expr; document.deleteString(elementForRemoving.getTextRange().getStartOffset(), elementForRemoving.getTextRange().getEndOffset()); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java index 52ad0c4cdde8..29ea4d39ac64 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java @@ -40,11 +40,9 @@ public abstract class SurroundPostfixTemplateBase extends StatementWrapPostfixTe @Override - public void expand(@NotNull PsiElement context, @NotNull final Editor editor) { + public final void expandForChooseExpression(@NotNull PsiElement context, @NotNull final Editor editor) { PsiElement topmostExpression = myPsiInfo.getTopmostExpression(context); - PsiElement expression = getWrappedExpression(topmostExpression); - assert topmostExpression != null; - PsiElement replace = topmostExpression.replace(expression); + PsiElement replace = getReplacedExpression(topmostExpression); TextRange range = PostfixTemplatesUtils.surround(getSurrounder(), editor, replace); if (range != null) { @@ -52,6 +50,12 @@ public abstract class SurroundPostfixTemplateBase extends StatementWrapPostfixTe } } + protected PsiElement getReplacedExpression(PsiElement topmostExpression) { + PsiElement expression = getWrappedExpression(topmostExpression); + assert topmostExpression != null; + return topmostExpression.replace(expression); + } + public boolean isStatement() { return false; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/TopmostExpressionSelector.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/TopmostExpressionSelector.java new file mode 100644 index 000000000000..bd6028d2d8d2 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/TopmostExpressionSelector.java @@ -0,0 +1,58 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.postfix.templates; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Condition; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + + +/** + * See {@link PostfixTemplateExpressionSelector} for description + */ +public class TopmostExpressionSelector implements PostfixTemplateExpressionSelector { + + @NotNull + private final Condition<PsiElement> myCondition; + + public TopmostExpressionSelector(@NotNull Condition<PsiElement> condition) { + + myCondition = condition; + } + + @Override + public boolean hasExpression(@NotNull PostfixTemplateWithExpressionSelector template, + @NotNull PsiElement context, + @NotNull Document copyDocument, + int newOffset) { + PsiElement topmostExpression = template.getPsiInfo().getTopmostExpression(context); + return topmostExpression != null && myCondition.value(topmostExpression); + } + + @Override + public void expandTemplate(@NotNull PostfixTemplateWithExpressionSelector template, + @NotNull PsiElement context, + @NotNull Editor editor) { + PostfixTemplatePsiInfo info = template.getPsiInfo(); + PsiElement expression = info.getTopmostExpression(context); + if (expression == null) { + return; + } + template.expandForChooseExpression(expression, editor); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/TypedPostfixTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/TypedPostfixTemplate.java deleted file mode 100644 index 0d7d424d99a8..000000000000 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/TypedPostfixTemplate.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.codeInsight.template.postfix.templates; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.Condition; -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.NotNull; - -public abstract class TypedPostfixTemplate extends PostfixTemplate { - - protected final PostfixTemplatePsiInfo myPsiInfo; - protected final Condition<PsiElement> myTypeChecker; - - protected TypedPostfixTemplate(@NotNull String name, - @NotNull String example, - @NotNull PostfixTemplatePsiInfo psiInfo, - @NotNull Condition<PsiElement> typeChecker) { - super(name, example); - this.myPsiInfo = psiInfo; - this.myTypeChecker = typeChecker; - } - - @Override - public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - PsiElement topmostExpression = myPsiInfo.getTopmostExpression(context); - return topmostExpression != null && myTypeChecker.value(topmostExpression); - } -} diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionManagerEx.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionManagerEx.java index 2c934d76bbd5..7ae94888c90d 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionManagerEx.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionManagerEx.java @@ -64,6 +64,8 @@ public class InspectionManagerEx extends InspectionManagerBase { @NotNull @Override protected ContentManager compute() { + ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); + toolWindowManager.registerToolWindow(ToolWindowId.INSPECTION, true, ToolWindowAnchor.BOTTOM, project); return ContentFactory.SERVICE.getInstance().createContentManager(new TabbedPaneContentUI(), true, project); } }; diff --git a/platform/lang-impl/src/com/intellij/execution/ProgramRunnerUtil.java b/platform/lang-impl/src/com/intellij/execution/ProgramRunnerUtil.java index f19d92f76bcf..b27a939dd57c 100644 --- a/platform/lang-impl/src/com/intellij/execution/ProgramRunnerUtil.java +++ b/platform/lang-impl/src/com/intellij/execution/ProgramRunnerUtil.java @@ -74,7 +74,7 @@ public class ProgramRunnerUtil { } public static void executeConfiguration(Project project, - DataContext context, + @Nullable DataContext context, @Nullable RunnerAndConfigurationSettings configuration, Executor executor, ExecutionTarget target, diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java index 6c0bfffd450e..9aab415d5e70 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java @@ -45,7 +45,9 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.ExceptionUtil; import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.SafeFileOutputStream; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.xml.XppReader; @@ -56,7 +58,6 @@ import org.xmlpull.v1.XmlSerializer; import java.awt.event.KeyEvent; import java.io.*; -import java.util.ArrayList; import java.util.List; import java.util.ListIterator; @@ -65,6 +66,8 @@ import java.util.ListIterator; */ public class ConsoleHistoryController { + private static final int VERSION = 1; + private static final Logger LOG = Logger.getInstance("com.intellij.execution.console.ConsoleHistoryController"); private final LanguageConsoleImpl myConsole; @@ -394,7 +397,15 @@ public class ConsoleHistoryController { } } catch (Exception ex) { - LOG.error(ex); + //noinspection ThrowableResultOfMethodCallIgnored + Throwable cause = ExceptionUtil.getRootCause(ex); + if (cause instanceof EOFException) { + LOG.warn("Failed to load " + myType + " console history from: " + file.getPath(), ex); + return false; + } + else { + LOG.error(ex); + } } finally { if (xmlReader != null) { @@ -423,6 +434,7 @@ public class ConsoleHistoryController { } serializer.setOutput(os = new SafeFileOutputStream(file), CharsetToolkit.UTF8); saveHistory(serializer); + serializer.flush(); } catch (Exception ex) { LOG.error(ex); @@ -439,47 +451,58 @@ public class ConsoleHistoryController { } @Nullable - private String loadHistory(final HierarchicalStreamReader in, final String expectedId) { + private String loadHistory(HierarchicalStreamReader in, String expectedId) { if (!in.getNodeName().equals("console-history")) return null; - final String id = in.getAttribute("id"); + int version = StringUtil.parseInt(in.getAttribute("version"), 0); + String id = in.getAttribute("id"); if (!expectedId.equals(id)) return null; - final ArrayList<String> entries = new ArrayList<String>(); + List<String> entries = ContainerUtil.newArrayList(); String consoleContent = null; while (in.hasMoreChildren()) { in.moveDown(); if ("history-entry".equals(in.getNodeName())) { - entries.add(in.getValue()); + entries.add(StringUtil.notNullize(in.getValue())); } else if ("console-content".equals(in.getNodeName())) { - consoleContent = in.getValue(); + consoleContent = StringUtil.notNullize(in.getValue()); } in.moveUp(); } for (ListIterator<String> iterator = entries.listIterator(entries.size()); iterator.hasPrevious(); ) { - final String entry = iterator.previous(); + String entry = iterator.previous(); getModel().addToHistory(entry); } return consoleContent; } - private void saveHistory(final XmlSerializer out) throws IOException { + private void saveHistory(XmlSerializer out) throws IOException { out.startDocument(CharsetToolkit.UTF8, null); out.startTag(null, "console-history"); + out.attribute(null, "version", String.valueOf(VERSION)); out.attribute(null, "id", myId); - for (String s : getModel().getHistory()) { - out.startTag(null, "history-entry"); - out.text(s); - out.endTag(null, "history-entry"); + try { + for (String s : getModel().getHistory()) { + textTag(out, "history-entry", s); + } + String current = myContent; + if (StringUtil.isNotEmpty(current)) { + textTag(out, "console-content", current); + } } - String current = myContent; - if (StringUtil.isNotEmpty(current)) { - out.startTag(null, "console-content"); - out.text(current); - out.endTag(null, "console-content"); + finally { + out.endTag(null, "console-history"); + out.endDocument(); } - out.endTag(null, "console-history"); - out.endDocument(); } } + private static void textTag(@NotNull XmlSerializer out, @NotNull String tag, @NotNull String text) throws IOException { + out.startTag(null, tag); + try { + out.cdsect(text); + } + finally { + out.endTag(null, tag); + } + } } diff --git a/platform/lang-impl/src/com/intellij/execution/console/DuplexConsoleView.java b/platform/lang-impl/src/com/intellij/execution/console/DuplexConsoleView.java index c760310cd391..87e1323fe607 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/DuplexConsoleView.java +++ b/platform/lang-impl/src/com/intellij/execution/console/DuplexConsoleView.java @@ -9,6 +9,7 @@ import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.execution.ui.ObservableConsoleView; import com.intellij.icons.AllIcons; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -34,6 +35,8 @@ public class DuplexConsoleView<S extends ConsoleView, T extends ConsoleView> ext private final S myPrimaryConsoleView; @NotNull private final T mySecondaryConsoleView; + @Nullable + private final String myStateStorageKey; private boolean myPrimary; @Nullable @@ -41,25 +44,46 @@ public class DuplexConsoleView<S extends ConsoleView, T extends ConsoleView> ext @NotNull private final SwitchDuplexConsoleViewAction mySwitchConsoleAction; + public DuplexConsoleView(@NotNull S primaryConsoleView, @NotNull T secondaryConsoleView) { + this(primaryConsoleView, secondaryConsoleView, null); + } + + public DuplexConsoleView(@NotNull S primaryConsoleView, @NotNull T secondaryConsoleView, @Nullable String stateStorageKey) { super(new CardLayout()); myPrimaryConsoleView = primaryConsoleView; mySecondaryConsoleView = secondaryConsoleView; + myStateStorageKey = stateStorageKey; add(myPrimaryConsoleView.getComponent(), PRIMARY_CONSOLE_PANEL); add(mySecondaryConsoleView.getComponent(), SECONDARY_CONSOLE_PANEL); - mySwitchConsoleAction = new SwitchDuplexConsoleViewAction(this); + mySwitchConsoleAction = new SwitchDuplexConsoleViewAction(); myPrimary = true; - enableConsole(false); + enableConsole(getStoredState()); Disposer.register(this, myPrimaryConsoleView); Disposer.register(this, mySecondaryConsoleView); } - public static <S extends ConsoleView, T extends ConsoleView> DuplexConsoleView<S, T> create(S primary, T secondary) { - return new DuplexConsoleView<S, T>(primary, secondary); + public static <S extends ConsoleView, T extends ConsoleView> DuplexConsoleView<S, T> create(@NotNull S primary, + @NotNull T secondary, + @Nullable String stateStorageKey) { + return new DuplexConsoleView<S, T>(primary, secondary, stateStorageKey); + } + + private void setStoredState(boolean primary) { + if (myStateStorageKey != null) { + PropertiesComponent.getInstance().setValue(myStateStorageKey, String.valueOf(primary)); + } + } + + private boolean getStoredState() { + if (myStateStorageKey == null) { + return false; + } + return PropertiesComponent.getInstance().getBoolean(myStateStorageKey, false); } public void enableConsole(boolean primary) { @@ -215,23 +239,22 @@ public class DuplexConsoleView<S extends ConsoleView, T extends ConsoleView> ext return mySwitchConsoleAction.getTemplatePresentation(); } - private static class SwitchDuplexConsoleViewAction extends ToggleAction implements DumbAware { - private final DuplexConsoleView myConsole; + private class SwitchDuplexConsoleViewAction extends ToggleAction implements DumbAware { - public SwitchDuplexConsoleViewAction(final DuplexConsoleView console) { + public SwitchDuplexConsoleViewAction() { super(ExecutionBundle.message("run.configuration.show.command.line.action.name"), null, AllIcons.Debugger.ToolConsole); - myConsole = console; } @Override public boolean isSelected(final AnActionEvent event) { - return !myConsole.isPrimaryConsoleEnabled(); + return !isPrimaryConsoleEnabled(); } @Override public void setSelected(final AnActionEvent event, final boolean flag) { - myConsole.enableConsole(!flag); + enableConsole(!flag); + setStoredState(!flag); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { @@ -244,12 +267,12 @@ public class DuplexConsoleView<S extends ConsoleView, T extends ConsoleView> ext public void update(final AnActionEvent event) { super.update(event); final Presentation presentation = event.getPresentation(); - final boolean isRunning = myConsole.myProcessHandler != null && !myConsole.myProcessHandler.isProcessTerminated(); + final boolean isRunning = myProcessHandler != null && !myProcessHandler.isProcessTerminated(); if (isRunning) { presentation.setEnabled(true); } else { - myConsole.enableConsole(true); + enableConsole(true); presentation.putClientProperty(SELECTED_PROPERTY, false); presentation.setEnabled(false); } diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java index 9cb57be6ad9d..1a9afd1e25e5 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java @@ -670,12 +670,14 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { } } + @NotNull public Editor getCurrentEditor() { - return ObjectUtils.chooseNotNull(myCurrentEditor, myConsoleEditor); + return ObjectUtils.notNull(myCurrentEditor, myConsoleEditor); } + @NotNull public Language getLanguage() { - return myVirtualFile.getLanguage(); + return ObjectUtils.assertNotNull(myVirtualFile.getLanguage()); } public void setLanguage(@NotNull Language language) { diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java b/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java index ae6af9d1064e..edf1352a760c 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java @@ -49,6 +49,8 @@ import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.*; import java.util.List; @@ -345,6 +347,14 @@ extends BeforeRunTaskProvider<RunConfigurationBeforeRunProvider.RunConfigurableB mySettings = settings; init(); myJBList.setSelectedValue(mySelectedSettings, true); + myJBList.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() ==2) { + doOKAction(); + } + } + }); FontMetrics fontMetrics = myJBList.getFontMetrics(myJBList.getFont()); int maxWidth = fontMetrics.stringWidth("m") * 30; for (RunnerAndConfigurationSettings setting : settings) { diff --git a/platform/lang-impl/src/com/intellij/execution/runners/AbstractConsoleRunnerWithHistory.java b/platform/lang-impl/src/com/intellij/execution/runners/AbstractConsoleRunnerWithHistory.java index df7f7e1b2e1c..b4b3cd919ec4 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/AbstractConsoleRunnerWithHistory.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/AbstractConsoleRunnerWithHistory.java @@ -15,6 +15,9 @@ */ package com.intellij.execution.runners; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionHelper; import com.intellij.execution.ExecutionManager; @@ -129,38 +132,30 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole registerActionShortcuts(actions, getLanguageConsole().getConsoleEditor().getComponent()); registerActionShortcuts(actions, panel); panel.updateUI(); + showConsole(defaultExecutor, contentDescriptor); // Run myProcessHandler.startNotify(); } - private String constructConsoleTitle(final @NotNull String consoleTitle) { + protected String constructConsoleTitle(final @NotNull String consoleTitle) { if (shouldAddNumberToTitle()) { - List<RunContentDescriptor> consoles = ExecutionHelper.collectConsolesByDisplayName(myProject, new NotNullFunction<String, Boolean>() { - @NotNull - @Override - public Boolean fun(String dom) { - return dom.contains(consoleTitle); - } - }); + List<String> activeConsoleNames = getActiveConsoleNames(consoleTitle); int max = 0; - for (RunContentDescriptor dsc : consoles) { - ProcessHandler handler = dsc.getProcessHandler(); - if (handler != null && !handler.isProcessTerminated()) { - if (max == 0) { - max = 1; - } - try { - int num = Integer.parseInt(dsc.getDisplayName().substring(consoleTitle.length() + 1, dsc.getDisplayName().length() - 1)); - if (num > max) { - max = num; - } - } - catch (Exception ignored) { - //skip + for (String name : activeConsoleNames) { + if (max == 0) { + max = 1; + } + try { + int num = Integer.parseInt(name.substring(consoleTitle.length() + 1, name.length() - 1)); + if (num > max) { + max = num; } } + catch (Exception ignored) { + //skip + } } if (max >= 1) { return consoleTitle + "(" + (max + 1) + ")"; @@ -178,9 +173,9 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole return false; } - protected void showConsole(Executor defaultExecutor, RunContentDescriptor myDescriptor) { + protected void showConsole(Executor defaultExecutor, RunContentDescriptor contentDescriptor) { // Show in run toolwindow - ExecutionManager.getInstance(myProject).getContentManager().showRunContent(defaultExecutor, myDescriptor); + ExecutionManager.getInstance(myProject).getContentManager().showRunContent(defaultExecutor, contentDescriptor); } protected void finishConsole() { @@ -245,11 +240,13 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole public static AnAction createConsoleExecAction(@NotNull LanguageConsoleView console, @NotNull ProcessHandler processHandler, @NotNull ProcessBackedConsoleExecuteActionHandler consoleExecuteActionHandler) { - return new ConsoleExecuteAction(console, consoleExecuteActionHandler, consoleExecuteActionHandler.getEmptyExecuteAction(), consoleExecuteActionHandler); + return new ConsoleExecuteAction(console, consoleExecuteActionHandler, consoleExecuteActionHandler.getEmptyExecuteAction(), + consoleExecuteActionHandler); } protected AnAction createConsoleExecAction(@NotNull ProcessBackedConsoleExecuteActionHandler consoleExecuteActionHandler) { - return new ConsoleExecuteAction(myConsoleView, consoleExecuteActionHandler, consoleExecuteActionHandler.getEmptyExecuteAction(), consoleExecuteActionHandler); + return new ConsoleExecuteAction(myConsoleView, consoleExecuteActionHandler, consoleExecuteActionHandler.getEmptyExecuteAction(), + consoleExecuteActionHandler); } @SuppressWarnings("UnusedDeclaration") @@ -301,4 +298,31 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole public ProcessBackedConsoleExecuteActionHandler getConsoleExecuteActionHandler() { return myConsoleExecuteActionHandler; } + + protected List<String> getActiveConsoleNames(final String consoleTitle) { + return getActiveConsolesFromRunToolWindow(consoleTitle); + } + + protected List<String> getActiveConsolesFromRunToolWindow(final String consoleTitle) { + List<RunContentDescriptor> consoles = ExecutionHelper.collectConsolesByDisplayName(myProject, new NotNullFunction<String, Boolean>() { + @NotNull + @Override + public Boolean fun(String dom) { + return dom.contains(consoleTitle); + } + }); + + return FluentIterable.from(consoles).filter(new Predicate<RunContentDescriptor>() { + @Override + public boolean apply(RunContentDescriptor input) { + ProcessHandler handler = input.getProcessHandler(); + return handler != null && !handler.isProcessTerminated(); + } + }).transform(new Function<RunContentDescriptor, String>() { + @Override + public String apply(RunContentDescriptor input) { + return input.getDisplayName(); + } + }).toList(); + } } diff --git a/platform/lang-impl/src/com/intellij/execution/runners/RestartAction.java b/platform/lang-impl/src/com/intellij/execution/runners/RestartAction.java index 507864126430..20b29c2de994 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/RestartAction.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/RestartAction.java @@ -61,6 +61,15 @@ public class RestartAction extends FakeRerunAction implements DumbAware, AnActio myDescriptor = descriptor; myExecutor = executor; // see IDEADEV-698 + + if (descriptor.getRestarter() == null) { + descriptor.setRestarter(new Runnable() { + @Override + public void run() { + restart(); + } + }); + } } @Override diff --git a/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java b/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java index ab9433e66831..bb6d206f1eb9 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java @@ -206,12 +206,6 @@ public class RunContentBuilder extends LogConsoleManagerBase { final RestartAction restartAction = new RestartAction(myExecutor, myRunner, contentDescriptor, getEnvironment()); restartAction.registerShortcut(component); actionGroup.add(restartAction); - contentDescriptor.setRestarter(new Runnable() { - @Override - public void run() { - restartAction.restart(); - } - }); if (myExecutionResult instanceof DefaultExecutionResult) { final AnAction[] actions = ((DefaultExecutionResult)myExecutionResult).getRestartActions(); diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java index 897d169e1845..821d9dbd6bc3 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java @@ -1271,6 +1271,17 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac return null; } + @Nullable + public void restoreContent(final String key) { + for (AnAction action : myMinimizedViewActions.getChildren(null)) { + Content content = ((RestoreViewAction)action).getContent(); + if (key.equals(content.getUserData(ViewImpl.ID))) { + action.actionPerformed(null); + return; + } + } + } + public void setToDisposeRemovedContent(final boolean toDispose) { myToDisposeRemovedContent = toDispose; } diff --git a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java index 8a36638d993c..9613ac0dd33f 100644 --- a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java +++ b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java @@ -392,6 +392,9 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data if (secondaryActionsAvailable()) { actionGroup.addAction(new ToggleInCommentsAction(this)).setAsSecondary(true); actionGroup.addAction(new ToggleInLiteralsOnlyAction(this)).setAsSecondary(true); + actionGroup.addAction(new ToggleExceptCommentsAction(this)).setAsSecondary(true); + actionGroup.addAction(new ToggleExceptLiteralsAction(this)).setAsSecondary(true); + actionGroup.addAction(new ToggleExceptCommentsAndLiteralsAction(this)).setAsSecondary(true); } actionGroup.addAction(new TogglePreserveCaseAction(this)); actionGroup.addAction(new ToggleSelectionOnlyAction(this)); @@ -473,8 +476,7 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data to.setCaseSensitive(from.isCaseSensitive()); to.setWholeWordsOnly(from.isWholeWordsOnly()); to.setRegularExpressions(from.isRegularExpressions()); - to.setInCommentsOnly(from.isInCommentsOnly()); - to.setInStringLiteralsOnly(from.isInStringLiteralsOnly()); + to.setSearchContext(from.getSearchContext()); if (from.isReplaceState()) { to.setPreserveCase(from.isPreserveCase()); } diff --git a/platform/lang-impl/src/com/intellij/find/FindSettings.java b/platform/lang-impl/src/com/intellij/find/FindSettings.java index 964670a45723..d61fd395eadd 100644 --- a/platform/lang-impl/src/com/intellij/find/FindSettings.java +++ b/platform/lang-impl/src/com/intellij/find/FindSettings.java @@ -119,4 +119,13 @@ public abstract class FindSettings{ public abstract boolean isInCommentsOnly(); public abstract void setInCommentsOnly(boolean selected); + + public abstract boolean isExceptStringLiterals(); + public abstract void setExceptStringLiterals(boolean selected); + + public abstract boolean isExceptComments(); + public abstract void setExceptComments(boolean selected); + + public abstract boolean isExceptCommentsAndLiterals(); + public abstract void setExceptCommentsAndLiterals(boolean selected); } diff --git a/platform/lang-impl/src/com/intellij/find/FindUtil.java b/platform/lang-impl/src/com/intellij/find/FindUtil.java index edc906481398..674358a49379 100644 --- a/platform/lang-impl/src/com/intellij/find/FindUtil.java +++ b/platform/lang-impl/src/com/intellij/find/FindUtil.java @@ -321,7 +321,7 @@ public class FindUtil { public static void searchBack(final Project project, final Editor editor, @Nullable DataContext context) { FindManager findManager = FindManager.getInstance(project); - if (!findManager.findWasPerformed()) { + if (!findManager.findWasPerformed() && !findManager.selectNextOccurrenceWasPerformed()) { new IncrementalFindAction().getHandler().execute(editor, context); return; } @@ -363,7 +363,7 @@ public class FindUtil { public static boolean searchAgain(final Project project, final Editor editor, @Nullable DataContext context) { FindManager findManager = FindManager.getInstance(project); - if (!findManager.findWasPerformed()) { + if (!findManager.findWasPerformed() && !findManager.selectNextOccurrenceWasPerformed()) { new IncrementalFindAction().getHandler().execute(editor, context); return false; } diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAction.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAction.java new file mode 100644 index 000000000000..ae60f31a70b7 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAction.java @@ -0,0 +1,38 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.find.editorHeaderActions; + +import com.intellij.find.EditorSearchComponent; +import com.intellij.find.FindModel; +import com.intellij.openapi.actionSystem.AnActionEvent; + +public class ToggleExceptCommentsAction extends EditorHeaderToggleAction implements SecondaryHeaderAction { + private static final String TEXT = "Except C&omments"; + + public ToggleExceptCommentsAction(EditorSearchComponent editorSearchComponent) { + super(editorSearchComponent, TEXT); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return getEditorSearchComponent().getFindModel().isExceptComments(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.EXCEPT_COMMENTS : FindModel.SearchContext.ANY); + } +} diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAndLiteralsAction.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAndLiteralsAction.java new file mode 100644 index 000000000000..d3ff1015e610 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAndLiteralsAction.java @@ -0,0 +1,38 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.find.editorHeaderActions; + +import com.intellij.find.EditorSearchComponent; +import com.intellij.find.FindModel; +import com.intellij.openapi.actionSystem.AnActionEvent; + +public class ToggleExceptCommentsAndLiteralsAction extends EditorHeaderToggleAction implements SecondaryHeaderAction { + private static final String TEXT = "Except Comments and Li&terals"; + + public ToggleExceptCommentsAndLiteralsAction(EditorSearchComponent editorSearchComponent) { + super(editorSearchComponent, TEXT); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return getEditorSearchComponent().getFindModel().isExceptCommentsAndStringLiterals(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.EXCEPT_COMMENTS_AND_STRING_LITERALS : FindModel.SearchContext.ANY); + } +} diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptLiteralsAction.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptLiteralsAction.java new file mode 100644 index 000000000000..4b7f09c2b211 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptLiteralsAction.java @@ -0,0 +1,38 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.find.editorHeaderActions; + +import com.intellij.find.EditorSearchComponent; +import com.intellij.find.FindModel; +import com.intellij.openapi.actionSystem.AnActionEvent; + +public class ToggleExceptLiteralsAction extends EditorHeaderToggleAction implements SecondaryHeaderAction { + private static final String TEXT = "Except L&iterals"; + + public ToggleExceptLiteralsAction(EditorSearchComponent editorSearchComponent) { + super(editorSearchComponent, TEXT); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return getEditorSearchComponent().getFindModel().isExceptStringLiterals(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.EXCEPT_STRING_LITERALS : FindModel.SearchContext.ANY); + } +} diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInCommentsAction.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInCommentsAction.java index e6cae36868af..4d9d5ad44cac 100644 --- a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInCommentsAction.java +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInCommentsAction.java @@ -18,8 +18,6 @@ public class ToggleInCommentsAction extends EditorHeaderToggleAction implements @Override public void setSelected(AnActionEvent e, boolean state) { - FindModel findModel = getEditorSearchComponent().getFindModel(); - findModel.setInCommentsOnly(state); - if (state) findModel.setInStringLiteralsOnly(false); + getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.IN_COMMENTS : FindModel.SearchContext.ANY); } } diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInLiteralsOnlyAction.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInLiteralsOnlyAction.java index c4d47e024f5e..d8fc5d530095 100644 --- a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInLiteralsOnlyAction.java +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInLiteralsOnlyAction.java @@ -18,8 +18,6 @@ public class ToggleInLiteralsOnlyAction extends EditorHeaderToggleAction implem @Override public void setSelected(AnActionEvent e, boolean state) { - FindModel findModel = getEditorSearchComponent().getFindModel(); - findModel.setInStringLiteralsOnly(state); - if (state) findModel.setInCommentsOnly(false); + getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.IN_STRING_LITERALS : FindModel.SearchContext.ANY); } } diff --git a/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java b/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java index 7bc0e0c595f8..d7f7665a9299 100644 --- a/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java +++ b/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java @@ -25,6 +25,7 @@ import com.intellij.find.impl.FindManagerImpl; import com.intellij.find.replaceInProject.ReplaceInProjectManager; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -71,8 +72,14 @@ public class FindInProjectManager { findModel.setOpenInNewTab(toOpenInNewTab[0]); FindInProjectUtil.setDirectoryName(findModel, dataContext); - Editor editor = CommonDataKeys.EDITOR.getData(dataContext); - FindUtil.initStringToFindWithSelection(findModel, editor); + String text = PlatformDataKeys.PREDEFINED_TEXT.getData(dataContext); + if (text != null) { + FindModel.initStringToFindNoMultiline(findModel, text); + } + else { + Editor editor = CommonDataKeys.EDITOR.getData(dataContext); + FindUtil.initStringToFindWithSelection(findModel, editor); + } findManager.showFindDialog(findModel, new Runnable() { @Override diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/CommonFindUsagesDialog.java b/platform/lang-impl/src/com/intellij/find/findUsages/CommonFindUsagesDialog.java index fe47fb8558a6..3315eac0b979 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/CommonFindUsagesDialog.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/CommonFindUsagesDialog.java @@ -16,8 +16,8 @@ package com.intellij.find.findUsages; +import com.intellij.lang.HelpID; import com.intellij.lang.findUsages.DescriptiveNameUtil; -import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; @@ -26,7 +26,9 @@ import com.intellij.psi.search.PsiSearchHelper; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleTextAttributes; import com.intellij.usageView.UsageViewUtil; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -35,6 +37,7 @@ import javax.swing.*; */ public class CommonFindUsagesDialog extends AbstractFindUsagesDialog { @NotNull protected final PsiElement myPsiElement; + @Nullable private final String myHelpId; public CommonFindUsagesDialog(@NotNull PsiElement element, @NotNull Project project, @@ -46,6 +49,7 @@ public class CommonFindUsagesDialog extends AbstractFindUsagesDialog { super(project, findUsagesOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, isTextSearch(element, isSingleFile, handler), !isSingleFile && !element.getManager().isInProject(element)); myPsiElement = element; + myHelpId = ObjectUtils.chooseNotNull(handler.getHelpId(), HelpID.FIND_OTHER_USAGES); init(); } @@ -71,8 +75,9 @@ public class CommonFindUsagesDialog extends AbstractFindUsagesDialog { coloredComponent.append(DescriptiveNameUtil.getDescriptiveName(myPsiElement), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); } + @Nullable @Override - protected void doHelpAction() { - HelpManager.getInstance().invokeHelp(FindUsagesManager.getHelpID(myPsiElement)); + protected String getHelpId() { + return myHelpId; } } diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesHandler.java b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesHandler.java index a9291cf612ba..b0dcc459d55b 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesHandler.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesHandler.java @@ -83,6 +83,11 @@ public abstract class FindUsagesHandler { return PsiElement.EMPTY_ARRAY; } + @Nullable + protected String getHelpId() { + return FindUsagesManager.getHelpID(myPsiElement); + } + @NotNull public static FindUsagesOptions createFindUsagesOptions(@NotNull Project project, @Nullable final DataContext dataContext) { FindUsagesOptions findUsagesOptions = new FindUsagesOptions(project, dataContext); 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); diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterEx.java b/platform/lang-impl/src/com/intellij/formatting/FormatterEx.java index e9e8c130a6f4..f5417dfb5a25 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterEx.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterEx.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -116,7 +116,14 @@ public abstract class FormatterEx{ TextRange affectedRange); public abstract void setProgressTask(@NotNull FormattingProgressTask progressIndicator); - + + /** + * Calculates minimum spacing, allowed by formatting model (in columns) for a block starting at given offset, + * relative to its previous sibling block. + * Returns zero, if required block cannot be found at provided offset, or spacing cannot be calculated due to some other reason. + */ + public abstract int getSpacingForBlockAtOffset(FormattingModel model, int offset); + public interface IndentInfoStorage { void saveIndentInfo(@Nullable IndentInfo info, int startOffset); diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index e1366431efd0..7b40b786ff7a 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -25,6 +25,7 @@ import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -109,6 +110,58 @@ public class FormatterImpl extends FormatterEx } @Override + public int getSpacingForBlockAtOffset(FormattingModel model, int offset) { + Couple<Block> blockWithParent = getBlockAtOffset(null, model.getRootBlock(), offset); + if (blockWithParent == null) { + return 0; + } + Block parentBlock = blockWithParent.first; + Block targetBlock = blockWithParent.second; + if (parentBlock == null || targetBlock == null) { + return 0; + } + Block prevBlock = findPreviousSibling(parentBlock, targetBlock); + if (prevBlock == null) { + return 0; + } + SpacingImpl spacing = (SpacingImpl)parentBlock.getSpacing(prevBlock, targetBlock); + if (spacing == null) { + return 0; + } + return Math.max(spacing.getMinSpaces(), 0); + } + + private static Couple<Block> getBlockAtOffset(Block parent, Block block, int offset) { + TextRange textRange = block.getTextRange(); + int startOffset = textRange.getStartOffset(); + int endOffset = textRange.getEndOffset(); + if (startOffset == offset) { + return Couple.of(parent, block); + } + if (startOffset > offset || endOffset < offset || block.isLeaf()) { + return null; + } + for (Block subBlock : block.getSubBlocks()) { + Couple<Block> result = getBlockAtOffset(block, subBlock, offset); + if (result != null) { + return result; + } + } + return null; + } + + private static Block findPreviousSibling(Block parent, Block block) { + Block result = null; + for (Block subBlock : parent.getSubBlocks()) { + if (subBlock == block) { + return result; + } + result = subBlock; + } + return null; + } + + @Override public void format(final FormattingModel model, final CodeStyleSettings settings, final CommonCodeStyleSettings.IndentOptions indentOptions, final CommonCodeStyleSettings.IndentOptions javaIndentOptions, diff --git a/platform/lang-impl/src/com/intellij/framework/detection/impl/exclude/DetectionExcludesConfigurable.java b/platform/lang-impl/src/com/intellij/framework/detection/impl/exclude/DetectionExcludesConfigurable.java index 88d675b168de..6239cbbab4ac 100644 --- a/platform/lang-impl/src/com/intellij/framework/detection/impl/exclude/DetectionExcludesConfigurable.java +++ b/platform/lang-impl/src/com/intellij/framework/detection/impl/exclude/DetectionExcludesConfigurable.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. @@ -28,17 +28,21 @@ import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.ui.*; import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.border.CustomLineBorder; import com.intellij.ui.components.JBList; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -66,15 +70,33 @@ public class DetectionExcludesConfigurable implements Configurable { @NotNull public JComponent createComponent() { myEnabledDetectionCheckBox = new JCheckBox("Enable framework detection"); + myEnabledDetectionCheckBox.setBorder(new EmptyBorder(10, 10, 0, 0)); final JBList excludesList = new JBList(myModel); - excludesList.setCellRenderer(new ColoredListCellRenderer() { + final ColoredListCellRenderer renderer = new ColoredListCellRenderer() { + JPanel panel = new JPanel(new BorderLayout()); + { + panel.setBorder(new EmptyBorder(2, 10, 2, 0)); + panel.add(this); + } + @Override protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { + setIconTextGap(4); if (value instanceof ExcludeListItem) { ((ExcludeListItem)value).renderItem(this); + setBorder(new EmptyBorder(0, 10, 0, 0)); } } - }); + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected, boolean hasFocus) { + super.getListCellRendererComponent(list, value, index, selected, hasFocus); + panel.setBackground(UIUtil.getListBackground(selected)); + return panel; + } + }; + renderer.setMyBorder(new EmptyBorder(0,0,0,0)); + excludesList.setCellRenderer(renderer); final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(excludesList) .disableUpAction().disableDownAction() .setAddAction(new AnActionButtonRunnable() { @@ -83,9 +105,12 @@ public class DetectionExcludesConfigurable implements Configurable { doAddAction(button); } }); + if (Registry.is("ide.new.project.settings")) { + decorator.setPanelBorder(new CustomLineBorder(1, 0, 0, 0)); + } myMainPanel = new JPanel(new BorderLayout(0, 5)); myMainPanel.add(myEnabledDetectionCheckBox, BorderLayout.NORTH); - final LabeledComponent<JPanel> excludesComponent = LabeledComponent.create(decorator.createPanel(), "Exclude from detection:"); + final LabeledComponent<JPanel> excludesComponent = LabeledComponent.create(decorator.createPanel(), " Exclude from detection:"); myMainPanel.add(excludesComponent); myEnabledDetectionCheckBox.addActionListener(new ActionListener() { @Override diff --git a/platform/lang-impl/src/com/intellij/framework/detection/impl/exclude/InvalidExcludeListItem.java b/platform/lang-impl/src/com/intellij/framework/detection/impl/exclude/InvalidExcludeListItem.java index 6848b039ceed..0dcdde7c6297 100644 --- a/platform/lang-impl/src/com/intellij/framework/detection/impl/exclude/InvalidExcludeListItem.java +++ b/platform/lang-impl/src/com/intellij/framework/detection/impl/exclude/InvalidExcludeListItem.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 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,6 +17,7 @@ package com.intellij.framework.detection.impl.exclude; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.SimpleTextAttributes; +import com.intellij.util.ui.EmptyIcon; /** * @author nik @@ -51,6 +52,7 @@ class InvalidExcludeListItem extends ExcludeListItem { else { renderer.append(myFileUrl, SimpleTextAttributes.ERROR_ATTRIBUTES); } + renderer.setIcon(EmptyIcon.ICON_16); } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedFileAction.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedFileAction.java index 6d734d52a584..e86caec534f6 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedFileAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedFileAction.java @@ -15,6 +15,7 @@ */ package com.intellij.ide.actions; +import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.navigation.GotoRelatedItem; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.ui.popup.JBPopup; @@ -25,22 +26,25 @@ import org.jetbrains.annotations.Nullable; import java.util.List; /** - * @deprecated API compatibility. Utility methods will be moved to NavigationUtil + * @deprecated API compatibility. Utility methods moved to NavigationUtil. + * todo [neuro] REMOVE-ME when September Ends.. * @author gregsh */ public class GotoRelatedFileAction { /** - * @deprecated This method will be moved to NavigationUtil + * @deprecated + * @see com.intellij.codeInsight.navigation.NavigationUtil#getRelatedItemsPopup(java.util.List, String) */ public static JBPopup createPopup(List<? extends GotoRelatedItem> items, final String title) { - return GotoRelatedSymbolAction.createPopup(items, title); + return NavigationUtil.getRelatedItemsPopup(items, title); } /** - * @deprecated This method will be moved to NavigationUtil + * @deprecated + * @see com.intellij.codeInsight.navigation.NavigationUtil#collectRelatedItems(com.intellij.psi.PsiElement, com.intellij.openapi.actionSystem.DataContext) */ public static List<GotoRelatedItem> getItems(@NotNull PsiElement contextElement, @Nullable DataContext dataContext) { - return GotoRelatedSymbolAction.getItems(contextElement, dataContext); + return NavigationUtil.collectRelatedItems(contextElement, dataContext); } } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedSymbolAction.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedSymbolAction.java index 2870f806ab8a..229531b3ef53 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedSymbolAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedSymbolAction.java @@ -15,39 +15,19 @@ */ package com.intellij.ide.actions; -import com.intellij.ide.util.DefaultPsiElementCellRenderer; +import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.navigation.GotoRelatedItem; -import com.intellij.navigation.GotoRelatedProvider; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.ui.popup.PopupStep; -import com.intellij.openapi.ui.popup.util.BaseListPopupStep; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.JBColor; -import com.intellij.ui.SeparatorWithText; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.ui.popup.list.ListPopupImpl; -import com.intellij.ui.popup.list.PopupListElementRenderer; -import com.intellij.util.Processor; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.util.*; import java.util.List; /** @@ -66,210 +46,20 @@ public class GotoRelatedSymbolAction extends AnAction { PsiElement element = getContextElement(e.getDataContext()); if (element == null) return; - List<GotoRelatedItem> items = getItems(element, e.getDataContext()); + List<GotoRelatedItem> items = NavigationUtil.collectRelatedItems(element, e.getDataContext()); if (items.isEmpty()) return; if (items.size() == 1 && items.get(0).getElement() != null) { items.get(0).navigate(); return; } - createPopup(items, "Choose Target").showInBestPositionFor(e.getDataContext()); - } - - public static JBPopup createPopup(final List<? extends GotoRelatedItem> items, final String title) { - Object[] elements = new Object[items.size()]; - //todo[nik] move presentation logic to GotoRelatedItem class - final Map<PsiElement, GotoRelatedItem> itemsMap = new HashMap<PsiElement, GotoRelatedItem>(); - for (int i = 0; i < items.size(); i++) { - GotoRelatedItem item = items.get(i); - elements[i] = item.getElement() != null ? item.getElement() : item; - itemsMap.put(item.getElement(), item); - } - - return getPsiElementPopup(elements, itemsMap, title, new Processor<Object>() { - @Override - public boolean process(Object element) { - if (element instanceof PsiElement) { - //noinspection SuspiciousMethodCalls - itemsMap.get(element).navigate(); - } - else { - ((GotoRelatedItem)element).navigate(); - } - return true; - } - } - ); - } - - private static JBPopup getPsiElementPopup(final Object[] elements, final Map<PsiElement, GotoRelatedItem> itemsMap, - final String title, final Processor<Object> processor) { - - final Ref<Boolean> hasMnemonic = Ref.create(false); - final DefaultPsiElementCellRenderer renderer = new DefaultPsiElementCellRenderer() { - { - setFocusBorderEnabled(false); - } - - @Override - public String getElementText(PsiElement element) { - String customName = itemsMap.get(element).getCustomName(); - return (customName != null ? customName : super.getElementText(element)); - } - - @Override - protected Icon getIcon(PsiElement element) { - Icon customIcon = itemsMap.get(element).getCustomIcon(); - return customIcon != null ? customIcon : super.getIcon(element); - } - - @Override - public String getContainerText(PsiElement element, String name) { - String customContainerName = itemsMap.get(element).getCustomContainerName(); - - if (customContainerName != null) { - return customContainerName; - } - PsiFile file = element.getContainingFile(); - return file != null && !getElementText(element).equals(file.getName()) - ? "(" + file.getName() + ")" - : null; - } - - @Override - protected DefaultListCellRenderer getRightCellRenderer(Object value) { - return null; - } - - @Override - protected boolean customizeNonPsiElementLeftRenderer(ColoredListCellRenderer renderer, - JList list, - Object value, - int index, - boolean selected, - boolean hasFocus) { - final GotoRelatedItem item = (GotoRelatedItem)value; - Color color = list.getForeground(); - final SimpleTextAttributes nameAttributes = new SimpleTextAttributes(Font.PLAIN, color); - final String name = item.getCustomName(); - if (name == null) return false; - renderer.append(name, nameAttributes); - renderer.setIcon(item.getCustomIcon()); - return true; - } - - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - final JPanel component = (JPanel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (!hasMnemonic.get()) return component; - - final JPanel panelWithMnemonic = new JPanel(new BorderLayout()); - final int mnemonic = getMnemonic(value, itemsMap); - final JLabel label = new JLabel(""); - if (mnemonic != -1) { - label.setText(mnemonic + "."); - label.setDisplayedMnemonicIndex(0); - } - label.setPreferredSize(new JLabel("8.").getPreferredSize()); - - final JComponent leftRenderer = (JComponent)component.getComponents()[0]; - component.remove(leftRenderer); - panelWithMnemonic.setBorder(BorderFactory.createEmptyBorder(0, 7, 0, 0)); - panelWithMnemonic.setBackground(leftRenderer.getBackground()); - label.setBackground(leftRenderer.getBackground()); - panelWithMnemonic.add(label, BorderLayout.WEST); - panelWithMnemonic.add(leftRenderer, BorderLayout.CENTER); - component.add(panelWithMnemonic); - return component; - } - }; - final ListPopupImpl popup = new ListPopupImpl(new BaseListPopupStep<Object>(title, Arrays.asList(elements)) { - @Override - public boolean isSpeedSearchEnabled() { - return true; - } - - @Override - public String getIndexedString(Object value) { - if (value instanceof GotoRelatedItem) { - //noinspection ConstantConditions - return ((GotoRelatedItem)value).getCustomName(); - } - final PsiElement element = (PsiElement)value; - return renderer.getElementText(element) + " " + renderer.getContainerText(element, null); - } - - @Override - public PopupStep onChosen(Object selectedValue, boolean finalChoice) { - processor.process(selectedValue); - return super.onChosen(selectedValue, finalChoice); - } - }) { - }; - popup.getList().setCellRenderer(new PopupListElementRenderer(popup) { - Map<Object, String> separators = new HashMap<Object, String>(); - { - final ListModel model = popup.getList().getModel(); - String current = null; - boolean hasTitle = false; - for (int i = 0; i < model.getSize(); i++) { - final Object element = model.getElementAt(i); - final GotoRelatedItem item = itemsMap.get(element); - if (item != null && !StringUtil.equals(current, item.getGroup())) { - current = item.getGroup(); - separators.put(element, current); - if (!hasTitle && !StringUtil.isEmpty(current)) { - hasTitle = true; - } - } - } - - if (!hasTitle) { - separators.remove(model.getElementAt(0)); - } - } - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - final Component component = renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - final String separator = separators.get(value); - - if (separator != null) { - JPanel panel = new JPanel(new BorderLayout()); - panel.add(component, BorderLayout.CENTER); - final SeparatorWithText sep = new SeparatorWithText() { - @Override - protected void paintComponent(Graphics g) { - g.setColor(new JBColor(Color.WHITE, UIUtil.getSeparatorColor())); - g.fillRect(0,0,getWidth(), getHeight()); - super.paintComponent(g); - } - }; - sep.setCaption(separator); - panel.add(sep, BorderLayout.NORTH); - return panel; - } - return component; - } - }); - - popup.setMinimumSize(new Dimension(200, -1)); - - for (Object item : elements) { - final int mnemonic = getMnemonic(item, itemsMap); - if (mnemonic != -1) { - final Action action = createNumberAction(mnemonic, popup, itemsMap, processor); - popup.registerAction(mnemonic + "Action", KeyStroke.getKeyStroke(String.valueOf(mnemonic)), action); - popup.registerAction(mnemonic + "Action", KeyStroke.getKeyStroke("NUMPAD" + String.valueOf(mnemonic)), action); - hasMnemonic.set(true); - } - } - return popup; + NavigationUtil.getRelatedItemsPopup(items, "Choose Target").showInBestPositionFor(e.getDataContext()); } @TestOnly @NotNull public static List<GotoRelatedItem> getItems(@NotNull PsiFile psiFile, @Nullable Editor editor, @Nullable DataContext dataContext) { - return getItems(getContextElement(psiFile, editor), dataContext); + return NavigationUtil.collectRelatedItems(getContextElement(psiFile, editor), dataContext); } @Nullable @@ -294,65 +84,4 @@ public class GotoRelatedSymbolAction extends AnAction { } return contextElement; } - - @NotNull - public static List<GotoRelatedItem> getItems(@NotNull PsiElement contextElement, @Nullable DataContext dataContext) { - Set<GotoRelatedItem> items = ContainerUtil.newLinkedHashSet(); - for (GotoRelatedProvider provider : Extensions.getExtensions(GotoRelatedProvider.EP_NAME)) { - items.addAll(provider.getItems(contextElement)); - if (dataContext != null) { - items.addAll(provider.getItems(dataContext)); - } - } - sortByGroupNames(items); - return new ArrayList<GotoRelatedItem>(items); - } - - private static void sortByGroupNames(Set<GotoRelatedItem> items) { - Map<String, List<GotoRelatedItem>> map = new HashMap<String, List<GotoRelatedItem>>(); - for (GotoRelatedItem item : items) { - final String key = item.getGroup(); - if (!map.containsKey(key)) { - map.put(key, new ArrayList<GotoRelatedItem>()); - } - map.get(key).add(item); - } - final List<String> keys = new ArrayList<String>(map.keySet()); - Collections.sort(keys, new Comparator<String>() { - @Override - public int compare(String o1, String o2) { - return StringUtil.isEmpty(o1) ? 1 : StringUtil.isEmpty(o2) ? -1 : o1.compareTo(o2); - } - }); - items.clear(); - for (String key : keys) { - items.addAll(map.get(key)); - } - } - - private static Action createNumberAction(final int mnemonic, - final ListPopupImpl listPopup, - final Map<PsiElement, GotoRelatedItem> itemsMap, - final Processor<Object> processor) { - return new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - for (final Object item : listPopup.getListStep().getValues()) { - if (getMnemonic(item, itemsMap) == mnemonic) { - listPopup.setFinalRunnable(new Runnable() { - @Override - public void run() { - processor.process(item); - } - }); - listPopup.closeOk(null); - } - } - } - }; - } - - private static int getMnemonic(Object item, Map<PsiElement, GotoRelatedItem> itemsMap) { - return (item instanceof GotoRelatedItem ? (GotoRelatedItem)item : itemsMap.get((PsiElement)item)).getMnemonic(); - } } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SearchAgainAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SearchAgainAction.java index 3e28023a935c..531f3e52159f 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchAgainAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchAgainAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -49,7 +49,8 @@ public class SearchAgainAction extends AnAction implements DumbAware { public void run() { PsiDocumentManager.getInstance(project).commitAllDocuments(); IdeDocumentHistory.getInstance(project).includeCurrentCommandAsNavigation(); - if(FindManager.getInstance(project).findNextUsageInEditor(editor)) { + FindManager findManager = FindManager.getInstance(project); + if(!findManager.selectNextOccurrenceWasPerformed() && findManager.findNextUsageInEditor(editor)) { return; } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SearchBackAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SearchBackAction.java index 4783205ae8c0..5f6e0d9959bd 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchBackAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchBackAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -46,7 +46,8 @@ public class SearchBackAction extends AnAction implements DumbAware { @Override public void run() { PsiDocumentManager.getInstance(project).commitAllDocuments(); - if(FindManager.getInstance(project).findPreviousUsageInEditor(editor)) { + FindManager findManager = FindManager.getInstance(project); + if(!findManager.selectNextOccurrenceWasPerformed() && findManager.findPreviousUsageInEditor(editor)) { return; } FindUtil.searchBack(project, editor, e.getDataContext()); diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java index ae7ee4363247..d704e738efa3 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java @@ -108,11 +108,9 @@ import javax.swing.border.EmptyBorder; import javax.swing.event.DocumentEvent; import java.awt.*; import java.awt.event.*; -import java.lang.reflect.Field; import java.util.*; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; /** * @author Konstantin Bulenkov @@ -2120,13 +2118,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA private SearchListModel() { super(); - try { - final Field field = DefaultListModel.class.getDeclaredField("delegate"); - field.setAccessible(true); - myDelegate = (Vector)field.get(this); - } - catch (NoSuchFieldException ignore) {} - catch (IllegalAccessException ignore) {} + myDelegate = ReflectionUtil.getField(DefaultListModel.class, this, Vector.class, "delegate"); } int next(int index) { diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java index bb80f55237e5..c4708c48d19f 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java @@ -29,6 +29,7 @@ import com.intellij.ide.projectView.ProjectView; import com.intellij.ide.projectView.impl.nodes.AbstractModuleNode; import com.intellij.ide.projectView.impl.nodes.AbstractProjectNode; import com.intellij.ide.projectView.impl.nodes.ModuleGroupNode; +import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode; import com.intellij.ide.util.treeView.*; import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.openapi.Disposable; @@ -54,6 +55,7 @@ import com.intellij.psi.util.PsiUtilCore; import com.intellij.refactoring.move.MoveHandler; import com.intellij.util.ArrayUtil; import com.intellij.util.ReflectionUtil; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; @@ -481,6 +483,27 @@ public abstract class AbstractProjectViewPane implements DataProvider, Disposabl } public PsiDirectory[] getSelectedDirectories() { + List<PsiDirectory> directories = ContainerUtil.newArrayList(); + for (PsiDirectoryNode node : getSelectedNodes(PsiDirectoryNode.class)) { + PsiDirectory directory = node.getValue(); + if (directory != null) { + directories.add(directory); + Object parentValue = node.getParent().getValue(); + if (parentValue instanceof PsiDirectory) { + while (true) { + directory = directory.getParentDirectory(); + if (directory == null || directory.equals(parentValue)) { + break; + } + directories.add(directory); + } + } + } + } + if (!directories.isEmpty()) { + return directories.toArray(new PsiDirectory[directories.size()]); + } + final PsiElement[] elements = getSelectedPSIElements(); if (elements.length == 1) { final PsiElement element = elements[0]; diff --git a/platform/lang-impl/src/com/intellij/ide/todo/ChangeListTodosPanel.java b/platform/lang-impl/src/com/intellij/ide/todo/ChangeListTodosPanel.java index 2aa89adeb3b5..2a6c396e1e3e 100644 --- a/platform/lang-impl/src/com/intellij/ide/todo/ChangeListTodosPanel.java +++ b/platform/lang-impl/src/com/intellij/ide/todo/ChangeListTodosPanel.java @@ -65,7 +65,12 @@ public abstract class ChangeListTodosPanel extends TodoPanel{ @Override public void changeListRenamed(final ChangeList list, final String oldName) { - setDisplayName(IdeBundle.message("changelist.todo.title", list.getName())); + AppUIUtil.invokeOnEdt(new Runnable() { + @Override + public void run() { + setDisplayName(IdeBundle.message("changelist.todo.title", list.getName())); + } + }); } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java b/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java index f260c732adf5..35f7f658edfd 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java +++ b/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java @@ -65,6 +65,7 @@ import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder; import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure; import com.intellij.util.Alarm; import com.intellij.util.ArrayUtil; +import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; import com.intellij.util.containers.HashSet; @@ -83,7 +84,6 @@ import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import java.awt.*; import java.awt.event.*; -import java.lang.reflect.Field; import java.util.*; import java.util.List; @@ -1018,22 +1018,13 @@ public class FileStructurePopup implements Disposable { public FileStructureTree(Object rootElement, boolean fastExpand) { super(new DefaultMutableTreeNode(rootElement)); if (fastExpand) { - boolean newValueIsSet; - try { - final Field field = JTree.class.getDeclaredField("expandedState"); - field.setAccessible(true); - field.set(this, new Hashtable() { - @Override - public synchronized Object get(Object key) { - return Boolean.TRUE; - } - }); - newValueIsSet = true; - } - catch (Exception e) { - newValueIsSet = false; - } - fast = newValueIsSet; + Hashtable hashtable = new Hashtable() { + @Override + public synchronized Object get(Object key) { + return Boolean.TRUE; + } + }; + fast = ReflectionUtil.setField(JTree.class, this, Hashtable.class, "expandedState", hashtable); } else { fast = false; diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java index dcc1429c8bbd..3e90f2310884 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java @@ -35,6 +35,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationAdapter; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.fileTypes.UnknownFileType; @@ -102,6 +103,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; public abstract class ChooseByNameBase { + private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.gotoByName.ChooseByNameBase"); protected final Project myProject; protected final ChooseByNameModel myModel; protected ChooseByNameItemProvider myProvider; @@ -133,7 +135,6 @@ public abstract class ChooseByNameBase { private final ListUpdater myListUpdater = new ListUpdater(); - private volatile boolean myListIsUpToDate = false; private boolean myDisposedFlag = false; private ActionCallback myPostponedOkAction; @@ -278,7 +279,7 @@ public abstract class ChooseByNameBase { if (PlatformDataKeys.HELP_ID.is(dataId)) { return myModel.getHelpId(); } - if (!myListIsUpToDate) { + if (myCalcElementsThread != null) { return null; } if (CommonDataKeys.PSI_ELEMENT.is(dataId)) { @@ -499,7 +500,7 @@ public abstract class ChooseByNameBase { myTextField.addFocusListener(new FocusAdapter() { @Override public void focusLost(@NotNull final FocusEvent e) { - cancelCalcElementsThread(); // cancel thread as early as possible + cancelListUpdater(); // cancel thread as early as possible myHideAlarm.addRequest(new Runnable() { @Override public void run() { @@ -531,7 +532,7 @@ public abstract class ChooseByNameBase { if (queue instanceof IdeEventQueue) { if (!((IdeEventQueue)queue).wasRootRecentlyClicked(oppositeComponent)) { Component root = SwingUtilities.getRoot(myTextField); - if (root != null) { + if (root != null && root.isShowing()) { root.requestFocus(); myTextField.requestFocus(); return; @@ -759,24 +760,29 @@ public abstract class ChooseByNameBase { } protected void doClose(final boolean ok) { - try { - if (checkDisposed()) return; + if (checkDisposed()) return; - if (postponeCloseWhenListReady(ok)) return; + if (postponeCloseWhenListReady(ok)) return; - cancelListUpdater(); - close(ok); + cancelListUpdater(); + close(ok); - clearPostponedOkAction(ok); - } - finally { - myListModel.clear(); - cancelCalcElementsThread(); - } + clearPostponedOkAction(ok); + myListModel.clear(); } protected void cancelListUpdater() { - cancelCalcElementsThread(); + final CalcElementsThread calcElementsThread = myCalcElementsThread; + if (calcElementsThread != null && calcElementsThread.cancel()) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + if (!checkDisposed() && calcElementsThread == myCalcElementsThread) { + backgroundCalculationFinished(Collections.emptyList(), 0); + } + } + }); + } myListUpdater.cancelAll(); } @@ -784,7 +790,7 @@ public abstract class ChooseByNameBase { if (!isToFixLostTyping()) return false; final String text = myTextField.getText(); - if (ok && !myListIsUpToDate && text != null && !text.trim().isEmpty()) { + if (ok && myCalcElementsThread != null && text != null && !text.trim().isEmpty()) { myPostponedOkAction = new ActionCallback(); IdeFocusManager.getInstance(myProject).typeAheadUntil(myPostponedOkAction); return true; @@ -885,7 +891,7 @@ public abstract class ChooseByNameBase { Disposer.register(myTextPopup, new Disposable() { @Override public void dispose() { - cancelCalcElementsThread(); + cancelListUpdater(); } }); myTextPopup.show(layeredPane); @@ -917,8 +923,6 @@ public abstract class ChooseByNameBase { return layeredPane; } - private final Object myRebuildMutex = new Object(); - protected void rebuildList(final int pos, final int delay, @NotNull final ModalityState modalityState, @@ -928,11 +932,13 @@ public abstract class ChooseByNameBase { return; } - myListIsUpToDate = false; myAlarm.cancelAllRequests(); myListUpdater.cancelAll(); - cancelCalcElementsThread(); + final CalcElementsThread calcElementsThread = myCalcElementsThread; + if (calcElementsThread != null) { + calcElementsThread.cancel(); + } final String text = myTextField.getText(); if (!canShowListForEmptyPattern() && @@ -960,24 +966,14 @@ public abstract class ChooseByNameBase { scheduleCalcElements(text, myCheckBox.isSelected(), modalityState, new Consumer<Set<?>>() { @Override public void consume(Set<?> elements) { - synchronized (myRebuildMutex) { - ApplicationManager.getApplication().assertIsDispatchThread(); - if (checkDisposed()) { - return; - } - - myListIsUpToDate = true; - setElementsToList(pos, elements); - myList.repaint(); - chosenElementMightChange(); - - if (elements.isEmpty()) { - myTextFieldPanel.hideHint(); - } + ApplicationManager.getApplication().assertIsDispatchThread(); + if (checkDisposed()) { + return; + } + backgroundCalculationFinished(elements, pos); - if (postRunnable != null) { - postRunnable.run(); - } + if (postRunnable != null) { + postRunnable.run(); } } }); @@ -992,6 +988,17 @@ public abstract class ChooseByNameBase { } } + private void backgroundCalculationFinished(Collection<?> result, int toSelect) { + myCalcElementsThread = null; + setElementsToList(toSelect, result); + myList.repaint(); + chosenElementMightChange(); + + if (result.isEmpty()) { + myTextFieldPanel.hideHint(); + } + } + public void scheduleCalcElements(String text, boolean checkboxState, ModalityState modalityState, @@ -1008,16 +1015,7 @@ public abstract class ChooseByNameBase { return myShowListAfterCompletionKeyStroke; } - private CalcElementsThread cancelCalcElementsThread() { - CalcElementsThread calcElementsThread = myCalcElementsThread; - if (calcElementsThread != null) { - calcElementsThread.cancel(); - myCalcElementsThread = null; - } - return calcElementsThread; - } - - private void setElementsToList(int pos, @NotNull Set<?> elements) { + private void setElementsToList(int pos, @NotNull Collection<?> elements) { myListUpdater.cancelAll(); if (checkDisposed()) return; if (elements.isEmpty()) { @@ -1202,33 +1200,12 @@ public abstract class ChooseByNameBase { } protected List<Object> getChosenElements() { - - List<Object> values = new ArrayList<Object>(Arrays.asList(myList.getSelectedValues())); - values.remove(EXTRA_ELEM); - values.remove(NON_PREFIX_SEPARATOR); - - if (myListIsUpToDate || !values.isEmpty()) { - return values; - } - - final String text = myTextField.getText(); - if (text.length() == 0) return Collections.emptyList(); - final boolean checkBoxState = myCheckBox.isSelected(); - final String[] names = ourLoadNamesEachTime ? ensureNamesLoaded(checkBoxState) : getNamesSync(checkBoxState); - if (names == null) return Collections.emptyList(); - - Object uniqueElement = null; - - for (final String name : names) { - if (text.equalsIgnoreCase(name)) { - final Object[] elements = myModel.getElementsByName(name, checkBoxState, text); - if (elements.length > 1) return Collections.emptyList(); - if (elements.length == 0) continue; - if (uniqueElement != null) return Collections.emptyList(); - uniqueElement = elements[0]; + return ContainerUtil.filter(myList.getSelectedValues(), new Condition<Object>() { + @Override + public boolean value(Object o) { + return o != EXTRA_ELEM && o != NON_PREFIX_SEPARATOR; } - } - return uniqueElement == null ? Collections.emptyList() : Collections.singletonList(uniqueElement); + }); } protected void chosenElementMightChange() { @@ -1565,6 +1542,7 @@ public abstract class ChooseByNameBase { @Override public void run() { if (!myCancelled.isCanceled()) { + LOG.assertTrue(myCalcElementsThread == CalcElementsThread.this); myCallback.consume(edt ? filter(elements) : filtered); } } @@ -1622,8 +1600,12 @@ public abstract class ChooseByNameBase { return elementsArray.size() >= myMaximumListSizeLimit; } - private void cancel() { + private boolean cancel() { + if (myCancelled.isCanceled()) { + return false; + } myCancelled.cancel(); + return true; } } @@ -1705,7 +1687,7 @@ public abstract class ChooseByNameBase { final LinkedHashSet<Object> nonPrefixMatchElementsArray = new LinkedHashSet<Object>(); hideHint(); ProgressManager.getInstance().run(new Task.Modal(myProject, prefixPattern, true) { - private ChooseByNameBase.CalcElementsThread myCalcElementsThread; + private ChooseByNameBase.CalcElementsThread myCalcUsagesThread; @Override public void run(@NotNull final ProgressIndicator indicator) { @@ -1716,7 +1698,7 @@ public abstract class ChooseByNameBase { @Override public void run() { final boolean[] overFlow = {false}; - myCalcElementsThread = new CalcElementsThread(text, everywhere, null, ModalityState.NON_MODAL, false) { + myCalcUsagesThread = new CalcElementsThread(text, everywhere, null, ModalityState.NON_MODAL, false) { private final AtomicBoolean userAskedToAbort = new AtomicBoolean(); @Override protected boolean isOverflow(@NotNull Set<Object> elementsArray) { @@ -1734,11 +1716,11 @@ public abstract class ChooseByNameBase { boolean anyPlace = isSearchInAnyPlace(); setSearchInAnyPlace(false); - myCalcElementsThread.addElementsByPattern(text, prefixMatchElementsArray, indicator, everywhere); + myCalcUsagesThread.addElementsByPattern(text, prefixMatchElementsArray, indicator, everywhere); setSearchInAnyPlace(anyPlace); if (anyPlace && !overFlow[0]) { - myCalcElementsThread.addElementsByPattern(text, nonPrefixMatchElementsArray, indicator, everywhere); + myCalcUsagesThread.addElementsByPattern(text, nonPrefixMatchElementsArray, indicator, everywhere); nonPrefixMatchElementsArray.removeAll(prefixMatchElementsArray); } @@ -1756,7 +1738,7 @@ public abstract class ChooseByNameBase { @Override public void onCancel() { - cancelCalcElementsThread(); + myCalcUsagesThread.cancel(); } }); } diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java index cb03b68bfadb..86d9c1782b2a 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java @@ -339,7 +339,7 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP } private static final Pattern patternToDetectLinesAndColumns = Pattern.compile("([^:]+)(?::|@|,|)\\[?(\\d+)?(?:(?:\\D)(\\d+)?)?\\]?"); - private static final Pattern patternToDetectAnonymousClasses = Pattern.compile("([\\.\\w]+)((\\$[\\d]+)*(\\$)?)"); + public static final Pattern patternToDetectAnonymousClasses = Pattern.compile("([\\.\\w]+)((\\$[\\d]+)*(\\$)?)"); private static final Pattern patternToDetectMembers = Pattern.compile("(.+)(#)(.*)"); @Override diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java index aa6a360c84e5..cc5915c26bb6 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java @@ -189,6 +189,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider @NotNull private static String getQualifierPattern(@NotNull ChooseByNameBase base, @NotNull String pattern) { + pattern = base.transformPattern(pattern); final String[] separators = base.getModel().getSeparators(); int lastSeparatorOccurrence = 0; for (String separator : separators) { diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionItemProvider.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionItemProvider.java index a6e6c8fdf658..99d3c9b338ae 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionItemProvider.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionItemProvider.java @@ -26,6 +26,7 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.impl.ActionManagerImpl; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.util.Function; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; @@ -128,17 +129,19 @@ public class GotoActionItemProvider implements ChooseByNameItemProvider { List<AnAction> actions = ContainerUtil.newArrayList(); if (everywhere) { for (String id : ((ActionManagerImpl)myActionManager).getActionIds()) { + ProgressManager.checkCanceled(); ContainerUtil.addIfNotNull(actions, myActionManager.getAction(id)); } } else { - actions.addAll(myModel.myActionsMap.keySet()); + actions.addAll(myModel.myActionGroups.keySet()); } List<ActionWrapper> actionWrappers = ContainerUtil.newArrayList(); for (AnAction action : actions) { + ProgressManager.checkCanceled(); MatchMode mode = myModel.actionMatches(pattern, action); if (mode != MatchMode.NONE) { - actionWrappers.add(new ActionWrapper(action, myModel.myActionsMap.get(action), mode, dataContext)); + actionWrappers.add(new ActionWrapper(action, myModel.myActionGroups.get(action), mode, dataContext)); } } return processItems(pattern, actionWrappers, consumer); diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java index 91f0d3368521..348283dc3885 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java @@ -66,16 +66,7 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel, C private Pattern myCompiledPattern; protected final SearchableOptionsRegistrar myIndex; - protected final Map<AnAction, String> myActionsMap = new TreeMap<AnAction, String>(new Comparator<AnAction>() { - @Override - public int compare(@NotNull AnAction o1, @NotNull AnAction o2) { - int compare = Comparing.compare(o1.getTemplatePresentation().getText(), o2.getTemplatePresentation().getText()); - if (compare == 0 && !o1.equals(o2)) { - return o1.hashCode() - o2.hashCode(); - } - return compare; - } - }); + protected final Map<AnAction, String> myActionGroups = ContainerUtil.newHashMap(); protected final Map<String, ApplyIntentionAction> myIntentions = new TreeMap<String, ApplyIntentionAction>(); private final Map<String, String> myConfigurablesNames = ContainerUtil.newTroveMap(); @@ -88,7 +79,7 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel, C myProject = project; myContextComponent = component; final ActionGroup mainMenu = (ActionGroup)myActionManager.getActionOrStub(IdeActions.GROUP_MAIN_MENU); - collectActions(myActionsMap, mainMenu, mainMenu.getTemplatePresentation().getText()); + collectActions(myActionGroups, mainMenu, mainMenu.getTemplatePresentation().getText()); if (project != null && editor != null && file != null) { final ApplyIntentionAction[] children = ApplyIntentionAction.getAvailableIntentions(editor, file); if (children != null) { @@ -98,6 +89,9 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel, C } } myIndex = SearchableOptionsRegistrar.getInstance(); + if (!EventQueue.isDispatchThread()) { + return; + } fillConfigurablesNames(ShowSettingsUtilImpl.getConfigurables(project, true)); } @@ -457,11 +451,14 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel, C else if (description != null && !description.equals(text) && matcher.matches(description, compiledPattern)) { return MatchMode.DESCRIPTION; } - final String groupName = myActionsMap.get(anAction); + if (text == null) { + return MatchMode.NONE; + } + final String groupName = myActionGroups.get(anAction); if (groupName == null) { - return text != null && matcher.matches(text, compiledPattern) ? MatchMode.NON_MENU : MatchMode.NONE; + return matcher.matches(text, compiledPattern) ? MatchMode.NON_MENU : MatchMode.NONE; } - return text != null && matcher.matches(groupName + " " + text, compiledPattern) ? MatchMode.GROUP : MatchMode.NONE; + return matcher.matches(groupName + " " + text, compiledPattern) ? MatchMode.GROUP : MatchMode.NONE; } @Nullable @@ -611,7 +608,7 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel, C PatternMatcher getMatcher() { return myMatcher.get(); } - + public static class ActionWrapper implements Comparable<ActionWrapper>{ private final AnAction myAction; private final MatchMode myMode; diff --git a/platform/lang-impl/src/com/intellij/internal/DumpLookupElementWeights.java b/platform/lang-impl/src/com/intellij/internal/DumpLookupElementWeights.java index 63d8a6038b6a..6350642c772e 100644 --- a/platform/lang-impl/src/com/intellij/internal/DumpLookupElementWeights.java +++ b/platform/lang-impl/src/com/intellij/internal/DumpLookupElementWeights.java @@ -22,7 +22,6 @@ import com.intellij.codeInsight.lookup.impl.LookupImpl; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; @@ -53,7 +52,12 @@ public class DumpLookupElementWeights extends AnAction implements DumbAware { } public static void dumpLookupElementWeights(final LookupImpl lookup) { - String sb = StringUtil.join(getLookupElementWeights(lookup), "\n"); + LookupElement selected = lookup.getCurrentItem(); + String sb = "selected: " + selected; + if (selected != null) { + sb += "\nprefix: " + lookup.itemPattern(selected); + } + sb += "\nweights:\n" + StringUtil.join(getLookupElementWeights(lookup), "\n"); System.out.println(sb); LOG.info(sb); } diff --git a/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java b/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java index ad322c232fa0..ccf63a429346 100644 --- a/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java +++ b/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java @@ -100,8 +100,7 @@ public class GeneratedParserUtilBase { if (!goodMarker) return false; ErrorState state = ErrorState.get(builder_); - Frame frame = state.frameStack.peekLast(); - return frame == null || frame.errorReportedAt <= builder_.rawTokenIndex(); + return !state.frameStack.isEmpty(); } public static TokenSet create_token_set_(IElementType... tokenTypes_) { @@ -399,6 +398,11 @@ public class GeneratedParserUtilBase { private static void enter_section_impl_(PsiBuilder builder_, int level, int modifiers, @Nullable String frameName) { ErrorState state = ErrorState.get(builder_); Frame frame = state.FRAMES.alloc().init(builder_, state, level, modifiers, frameName); + Frame prevFrame = state.frameStack.peekLast(); + if (prevFrame != null && prevFrame.errorReportedAt > frame.position) { + // report error for previous unsuccessful frame + reportError(builder_, state, frame, true, false); + } if (((frame.modifiers & _LEFT_) | (frame.modifiers & _LEFT_INNER_)) != 0) { PsiBuilder.Marker left = (PsiBuilder.Marker)builder_.getLatestDoneMarker(); if (invalid_left_marker_guard_(builder_, left, frameName)) { @@ -456,10 +460,10 @@ public class GeneratedParserUtilBase { state.clearVariants(true, frame.variantCount); addVariantInner(state, initialPos, frame.name); } + int lastErrorPos = getLastVariantPos(state, initialPos); if (!state.suppressErrors && eatMore != null) { state.suppressErrors = true; final boolean eatMoreFlagOnce = !builder_.eof() && eatMore.parse(builder_, frame.level + 1); - final int lastErrorPos = getLastVariantPos(state, initialPos); boolean eatMoreFlag = eatMoreFlagOnce || !result && frame.position == initialPos && lastErrorPos > frame.position; PsiBuilderImpl.ProductionMarker latestDoneMarker = @@ -497,7 +501,7 @@ public class GeneratedParserUtilBase { errorReported = reportError(builder_, state, frame, true, true); parseAsTree(state, builder_, frame.level + 1, DUMMY_BLOCK, true, TOKEN_ADVANCER, eatMore); } - else if (eatMoreFlagOnce || (!result && frame.position != builder_.rawTokenIndex())) { + else if (eatMoreFlagOnce || (!result && frame.position != builder_.rawTokenIndex()) || frame.errorReportedAt > initialPos) { errorReported = reportError(builder_, state, frame, true, false); } if (extensionMarker != null) { @@ -512,10 +516,14 @@ public class GeneratedParserUtilBase { } else if (!result && pinned && frame.errorReportedAt < 0) { // do not report if there are errors beyond current position - if (getLastVariantPos(state, initialPos) == initialPos) { + if (lastErrorPos == initialPos) { // do not force, inner recoverRoot might have skipped some tokens reportError(builder_, state, frame, false, false); } + else if (lastErrorPos > initialPos) { + // set error pos here as if it is reported for future reference + frame.errorReportedAt = lastErrorPos; + } } // propagate errorReportedAt up the stack to avoid duplicate reporting Frame prevFrame = willFail && eatMore == null ? null : state.frameStack.peekLast(); @@ -607,7 +615,7 @@ public class GeneratedParserUtilBase { return; } int position = builder_.rawTokenIndex(); - if (frame.errorReportedAt < position && getLastVariantPos(state, position) <= position) { + if (frame.errorReportedAt < position && getLastVariantPos(state, position + 1) <= position) { reportError(builder_, state, frame, true, advance); } } @@ -865,7 +873,7 @@ public class GeneratedParserUtilBase { ((modifiers & _LEFT_INNER_) != 0? "_LEFT_INNER_, ": "") + ((modifiers & _AND_) != 0? "_AND_, ": "") + ((modifiers & _NOT_) != 0? "_NOT_, ": ""); - return "<" + offset + ", " + mod + level + (errorReportedAt > -1 ? ", [" + errorReportedAt + "]" : "") + ">"; + return String.format("{%s:%s:%d, %d, %s%s}", offset, position, level, errorReportedAt, mod, name); } } diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectAllOccurrencesAction.java b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectAllOccurrencesAction.java index 3e284ff6b2cc..ae2a4dc0d53c 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectAllOccurrencesAction.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectAllOccurrencesAction.java @@ -42,8 +42,6 @@ public class SelectAllOccurrencesAction extends EditorAction { @Override public void doExecute(final Editor editor, @Nullable Caret c, DataContext dataContext) { - if (executeEquivalentFindPanelAction(editor, dataContext)) return; - Caret caret = c == null ? editor.getCaretModel().getPrimaryCaret() : c; boolean wholeWordsSearch = false; @@ -88,10 +86,5 @@ public class SelectAllOccurrencesAction extends EditorAction { }, caretShiftFromSelectionStart); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } - - @Override - protected EditorHeaderAction getEquivalentFindPanelAction(EditorSearchComponent searchComponent) { - return new SelectAllAction(searchComponent); - } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectNextOccurrenceAction.java b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectNextOccurrenceAction.java index 7d3c408ee152..74390f15b149 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectNextOccurrenceAction.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectNextOccurrenceAction.java @@ -40,8 +40,6 @@ public class SelectNextOccurrenceAction extends EditorAction { @Override public void doExecute(Editor editor, @Nullable Caret c, DataContext dataContext) { - if (executeEquivalentFindPanelAction(editor, dataContext)) return; - Caret caret = c == null ? editor.getCaretModel().getPrimaryCaret() : c; TextRange wordSelectionRange = getSelectionRange(editor, caret); boolean notFoundPreviously = getAndResetNotFoundStatus(editor); @@ -56,7 +54,7 @@ public class SelectNextOccurrenceAction extends EditorAction { FindModel model = getFindModel(selectedText, wholeWordSearch); - findManager.setFindWasPerformed(); + findManager.setSelectNextOccurrenceWasPerformed(); findManager.setFindNextModel(model); int searchStartOffset = notFoundPreviously ? 0 : caret.getSelectionEnd(); @@ -84,10 +82,5 @@ public class SelectNextOccurrenceAction extends EditorAction { } editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } - - @Override - protected EditorHeaderAction getEquivalentFindPanelAction(EditorSearchComponent searchComponent) { - return new AddOccurrenceAction(searchComponent); - } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectOccurrencesActionHandler.java b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectOccurrencesActionHandler.java index 7b189281dfd0..1858ef53b3f5 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectOccurrencesActionHandler.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectOccurrencesActionHandler.java @@ -107,25 +107,4 @@ abstract public class SelectOccurrencesActionHandler extends EditorActionHandler model.setWholeWordsOnly(wholeWords); return model; } - - protected boolean executeEquivalentFindPanelAction(Editor editor, DataContext context) { - if (editor.getHeaderComponent() instanceof EditorSearchComponent) { - EditorSearchComponent searchComponent = (EditorSearchComponent)editor.getHeaderComponent(); - EditorHeaderAction action = getEquivalentFindPanelAction(searchComponent); - if (action != null) { - Presentation presentation = new Presentation(); - AnActionEvent event = new AnActionEvent(null, context, ActionPlaces.MAIN_MENU, presentation, ActionManager.getInstance(), 0); - action.update(event); - if (presentation.isEnabled()) { - action.actionPerformed(event); - return true; - } - } - } - return false; - } - - protected EditorHeaderAction getEquivalentFindPanelAction(EditorSearchComponent searchComponent) { - return null; - } } diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/actions/UnselectPreviousOccurrenceAction.java b/platform/lang-impl/src/com/intellij/openapi/editor/actions/UnselectPreviousOccurrenceAction.java index fffff56b12dc..a5657ff89b27 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/actions/UnselectPreviousOccurrenceAction.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/actions/UnselectPreviousOccurrenceAction.java @@ -15,9 +15,6 @@ */ package com.intellij.openapi.editor.actions; -import com.intellij.find.EditorSearchComponent; -import com.intellij.find.editorHeaderActions.EditorHeaderAction; -import com.intellij.find.editorHeaderActions.RemoveOccurrenceAction; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; @@ -38,8 +35,6 @@ public class UnselectPreviousOccurrenceAction extends EditorAction { @Override public void doExecute(Editor editor, @Nullable Caret caret, DataContext dataContext) { - if (executeEquivalentFindPanelAction(editor, dataContext)) return; - if (editor.getCaretModel().getCaretCount() > 1) { editor.getCaretModel().removeCaret(editor.getCaretModel().getPrimaryCaret()); } @@ -49,10 +44,5 @@ public class UnselectPreviousOccurrenceAction extends EditorAction { getAndResetNotFoundStatus(editor); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } - - @Override - protected EditorHeaderAction getEquivalentFindPanelAction(EditorSearchComponent searchComponent) { - return new RemoveOccurrenceAction(searchComponent); - } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/FontMapper.java b/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/FontMapper.java index 4fec39342090..c5fd5592b634 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/FontMapper.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/FontMapper.java @@ -16,10 +16,10 @@ package com.intellij.openapi.editor.richcopy; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.ReflectionUtil; import org.jetbrains.annotations.NotNull; import java.awt.*; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Locale; @@ -46,20 +46,18 @@ public class FontMapper { } Method findFontMethod = Class.forName("sun.font.FontManager").getMethod("findFont2D", String.class, int.class, int.class); for (String logicalFont : logicalFontsToMap) { - String physicalFont = null; Object font2D = findFontMethod.invoke(fontManager, logicalFont, Font.PLAIN, 0); if (font2D == null) { continue; } String fontClassName = font2D.getClass().getName(); + String physicalFont = null; if ("sun.font.CompositeFont".equals(fontClassName)) { // Windows and Linux case Object physicalFontObject = Class.forName("sun.font.CompositeFont").getMethod("getSlotFont", int.class).invoke(font2D, 0); physicalFont = (String)Class.forName("sun.font.Font2D").getMethod("getFamilyName", Locale.class).invoke(physicalFontObject, Locale.getDefault()); } else if ("sun.font.CFont".equals(fontClassName)) { // MacOS case - Field field = Class.forName("sun.font.CFont").getDeclaredField("nativeFontName"); - field.setAccessible(true); - physicalFont = (String)field.get(font2D); + physicalFont = ReflectionUtil.getField(Class.forName("sun.font.CFont"), font2D, String.class, "nativeFontName"); } if (physicalFont != null) { logicalToPhysicalMapping.put(logicalFont, physicalFont); diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/TextWithMarkupProcessor.java b/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/TextWithMarkupProcessor.java index 54406106b976..504496a27716 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/TextWithMarkupProcessor.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/TextWithMarkupProcessor.java @@ -23,6 +23,7 @@ import com.intellij.ide.highlighter.HighlighterFactory; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.colors.FontPreferences; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.ex.DisposableIterator; import com.intellij.openapi.editor.ex.MarkupModelEx; @@ -418,7 +419,7 @@ public class TextWithMarkupProcessor extends CopyPastePostProcessor<RawTextWithM private MarkupIterator(@NotNull CharSequence charSequence, @NotNull RangeIterator rangeIterator, @NotNull EditorColorsScheme colorsScheme) { myRangeIterator = rangeIterator; - mySegmentIterator = new SegmentIterator(charSequence, colorsScheme.getEditorFontName(), colorsScheme.getEditorFontSize()); + mySegmentIterator = new SegmentIterator(charSequence, colorsScheme.getFontPreferences()); } public boolean atEnd() { @@ -812,8 +813,7 @@ public class TextWithMarkupProcessor extends CopyPastePostProcessor<RawTextWithM private static class SegmentIterator { private final CharSequence myCharSequence; - private final String myDefaultFontFamilyName; - private final int myFontSize; + private final FontPreferences myFontPreferences; private int myCurrentStartOffset; private int myCurrentOffset; @@ -822,10 +822,9 @@ public class TextWithMarkupProcessor extends CopyPastePostProcessor<RawTextWithM private String myCurrentFontFamilyName; private String myNextFontFamilyName; - private SegmentIterator(CharSequence charSequence, String defaultFontFamilyName, int fontSize) { + private SegmentIterator(CharSequence charSequence, FontPreferences fontPreferences) { myCharSequence = charSequence; - myDefaultFontFamilyName = defaultFontFamilyName; - myFontSize = fontSize; + myFontPreferences = fontPreferences; } public void reset(int startOffset, int endOffset, int fontStyle) { @@ -843,9 +842,8 @@ public class TextWithMarkupProcessor extends CopyPastePostProcessor<RawTextWithM myCurrentStartOffset = myCurrentOffset; for (; myCurrentOffset < myEndOffset; myCurrentOffset++) { FontInfo fontInfo = ComplementaryFontsRegistry.getFontAbleToDisplay(myCharSequence.charAt(myCurrentOffset), - myFontSize, myFontStyle, - myDefaultFontFamilyName); + myFontPreferences); String fontFamilyName = fontInfo.getFont().getFamily(); if (myCurrentFontFamilyName == null) { diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java index b1385686bc3a..09e489b3d1df 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java @@ -25,15 +25,14 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ModuleRootAdapter; -import com.intellij.openapi.roots.ModuleRootEvent; -import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.*; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.newvfs.BulkFileListener; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; +import com.intellij.util.IncorrectOperationException; import com.intellij.util.Query; import com.intellij.util.containers.ConcurrentIntObjectMap; import com.intellij.util.containers.StripedLockIntObjectConcurrentHashMap; @@ -43,6 +42,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; +import java.util.Arrays; +import java.util.Collections; import java.util.List; public class DirectoryIndexImpl extends DirectoryIndex { @@ -68,7 +69,7 @@ public class DirectoryIndexImpl extends DirectoryIndex { }); } - private void subscribeToFileChanges() { + protected void subscribeToFileChanges() { myConnection.subscribe(FileTypeManager.TOPIC, new FileTypeListener.Adapter() { @Override public void fileTypesChanged(@NotNull FileTypeEvent event) { @@ -98,7 +99,7 @@ public class DirectoryIndexImpl extends DirectoryIndex { }); } - private void markContentRootsForRefresh() { + protected void markContentRootsForRefresh() { Module[] modules = ModuleManager.getInstance(myProject).getModules(); for (Module module : modules) { VirtualFile[] contentRoots = ModuleRootManager.getInstance(module).getContentRoots(); @@ -110,7 +111,7 @@ public class DirectoryIndexImpl extends DirectoryIndex { } } - private void dispatchPendingEvents() { + protected void dispatchPendingEvents() { myConnection.deliverImmediately(); } @@ -124,24 +125,27 @@ public class DirectoryIndexImpl extends DirectoryIndex { private RootIndex getRootIndex() { RootIndex rootIndex = myRootIndex; if (rootIndex == null) { - myRootIndex = rootIndex = new RootIndex(myProject, new RootIndex.InfoCache() { - // Upsource can't use int-mapping because different files may have the same id there - private final ConcurrentIntObjectMap<DirectoryInfo> myInfoCache = new StripedLockIntObjectConcurrentHashMap<DirectoryInfo>(); - @Override - public void cacheInfo(@NotNull VirtualFile dir, @NotNull DirectoryInfo info) { - myInfoCache.put(((NewVirtualFile)dir).getId(), info); - } - - @Override - public DirectoryInfo getCachedInfo(@NotNull VirtualFile dir) { - return myInfoCache.get(((NewVirtualFile)dir).getId()); - } - }); + myRootIndex = rootIndex = new RootIndex(myProject, createRootInfoCache()); } return rootIndex; } - @Override + protected RootIndex.InfoCache createRootInfoCache() { + return new RootIndex.InfoCache() { + // Upsource can't use int-mapping because different files may have the same id there + private final ConcurrentIntObjectMap<DirectoryInfo> myInfoCache = new StripedLockIntObjectConcurrentHashMap<DirectoryInfo>(); + @Override + public void cacheInfo(@NotNull VirtualFile dir, @NotNull DirectoryInfo info) { + myInfoCache.put(((NewVirtualFile)dir).getId(), info); + } + + @Override + public DirectoryInfo getCachedInfo(@NotNull VirtualFile dir) { + return myInfoCache.get(((NewVirtualFile)dir).getId()); + } + }; + } + @TestOnly public void checkConsistency() { getRootIndex().checkConsistency(); @@ -181,6 +185,21 @@ public class DirectoryIndexImpl extends DirectoryIndex { return getRootIndex().getPackageName(dir); } + @NotNull + @Override + public OrderEntry[] getOrderEntries(@NotNull DirectoryInfo info) { + checkAvailability(); + return getRootIndex().getOrderEntries(info); + } + + @TestOnly + void assertConsistency(DirectoryInfo info) { + OrderEntry[] entries = getOrderEntries(info); + for (int i = 1; i < entries.length; i++) { + assert RootIndex.BY_OWNER_MODULE.compare(entries[i - 1], entries[i]) <= 0; + } + } + private void checkAvailability() { if (myDisposed) { ProgressManager.checkCanceled(); @@ -188,4 +207,95 @@ public class DirectoryIndexImpl extends DirectoryIndex { } } + @NotNull + private static OrderEntry createFakeOrderEntry(@NotNull final Module ownerModule) { + return new OrderEntry() { + @NotNull + @Override + public VirtualFile[] getFiles(OrderRootType type) { + throw new IncorrectOperationException(); + } + + @NotNull + @Override + public String[] getUrls(OrderRootType rootType) { + throw new IncorrectOperationException(); + } + + @NotNull + @Override + public String getPresentableName() { + throw new IncorrectOperationException(); + } + + @Override + public boolean isValid() { + throw new IncorrectOperationException(); + } + + @NotNull + @Override + public Module getOwnerModule() { + return ownerModule; + } + + @Override + public <R> R accept(RootPolicy<R> policy, @Nullable R initialValue) { + throw new IncorrectOperationException(); + } + + @Override + public int compareTo(@NotNull OrderEntry o) { + throw new IncorrectOperationException(); + } + + @Override + public boolean isSynthetic() { + throw new IncorrectOperationException(); + } + }; + } + + @Override + @Nullable + OrderEntry findOrderEntryWithOwnerModule(@NotNull DirectoryInfo info, @NotNull Module ownerModule) { + OrderEntry[] entries = getOrderEntries(info); + if (entries.length < 10) { + for (OrderEntry entry : entries) { + if (entry.getOwnerModule() == ownerModule) return entry; + } + return null; + } + int index = Arrays.binarySearch(entries, createFakeOrderEntry(ownerModule), RootIndex.BY_OWNER_MODULE); + return index < 0 ? null : entries[index]; + } + + @Override + @NotNull + List<OrderEntry> findAllOrderEntriesWithOwnerModule(@NotNull DirectoryInfo info, @NotNull Module ownerModule) { + OrderEntry[] entries = getOrderEntries(info); + if (entries.length == 0) return Collections.emptyList(); + + if (entries.length == 1) { + OrderEntry entry = entries[0]; + return entry.getOwnerModule() == ownerModule ? Arrays.asList(entries) : Collections.<OrderEntry>emptyList(); + } + int index = Arrays.binarySearch(entries, createFakeOrderEntry(ownerModule), RootIndex.BY_OWNER_MODULE); + if (index < 0) { + return Collections.emptyList(); + } + int firstIndex = index; + while (firstIndex - 1 >= 0 && entries[firstIndex - 1].getOwnerModule() == ownerModule) { + firstIndex--; + } + int lastIndex = index + 1; + while (lastIndex < entries.length && entries[lastIndex].getOwnerModule() == ownerModule) { + lastIndex++; + } + + OrderEntry[] subArray = new OrderEntry[lastIndex - firstIndex]; + System.arraycopy(entries, firstIndex, subArray, 0, lastIndex - firstIndex); + + return Arrays.asList(subArray); + } } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java index 932204c6dbd3..4ba8b7b03a8d 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java @@ -72,10 +72,8 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl { private Set<LocalFileSystem.WatchRequest> myRootsToWatch = new THashSet<LocalFileSystem.WatchRequest>(); private final boolean myDoLogCachesUpdate; - public ProjectRootManagerComponent(Project project, - DirectoryIndex directoryIndex, - StartupManager startupManager) { - super(project, directoryIndex); + public ProjectRootManagerComponent(Project project, StartupManager startupManager) { + super(project); myConnection = project.getMessageBus().connect(project); myConnection.subscribe(FileTypeManager.TOPIC, new FileTypeListener() { diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/CheckboxTreeTable.java b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/CheckboxTreeTable.java deleted file mode 100644 index dd72da957fa2..000000000000 --- a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/CheckboxTreeTable.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.roots.libraries.ui.impl; - -import com.intellij.ui.CheckboxTree; -import com.intellij.ui.CheckedTreeNode; -import com.intellij.ui.ClickListener; -import com.intellij.ui.dualView.TreeTableView; -import com.intellij.ui.treeStructure.treetable.ListTreeTableModelOnColumns; -import com.intellij.ui.treeStructure.treetable.TreeTableTree; -import com.intellij.util.ui.ColumnInfo; -import com.intellij.util.ui.tree.TreeUtil; -import org.jetbrains.annotations.NotNull; - -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; -import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Enumeration; - -/** - * @author nik - */ -public class CheckboxTreeTable extends TreeTableView { - public CheckboxTreeTable(CheckedTreeNode root, CheckboxTree.CheckboxTreeCellRenderer renderer, final ColumnInfo[] columns) { - super(new ListTreeTableModelOnColumns(root, columns)); - initTree(getTree(), renderer); - } - - //todo[nik] I hate to copy-paste but have to copy the code below from CheckboxTreeBase to support CheckboxTree inside TreeTable in IDEA 11.1.x branch - //todo[nik] I solemnly swear to get rid of this code in IDEA 12 branch - private void initTree(final TreeTableTree tree, final CheckboxTree.CheckboxTreeCellRenderer cellRenderer) { - tree.setCellRenderer(cellRenderer); - tree.setRootVisible(false); - tree.setShowsRootHandles(true); - tree.setLineStyleAngled(); - TreeUtil.installActions(tree); - - new ClickListener() { - @Override - public boolean onClick(@NotNull MouseEvent e, int clickCount) { - int row = tree.getRowForLocation(e.getX(), e.getY()); - if (row < 0) return false; - final Object o = tree.getPathForRow(row).getLastPathComponent(); - if (!(o instanceof CheckedTreeNode)) return false; - Rectangle rowBounds = tree.getRowBounds(row); - cellRenderer.setBounds(rowBounds); - Rectangle checkBounds = cellRenderer.myCheckbox.getBounds(); - checkBounds.setLocation(rowBounds.getLocation()); - - if (checkBounds.height == 0) checkBounds.height = rowBounds.height; - - final CheckedTreeNode node = (CheckedTreeNode)o; - if (checkBounds.contains(e.getPoint())) { - if (node.isEnabled()) { - toggleNode(node); - tree.setSelectionRow(row); - return true; - } - } - - return false; - } - }.installOn(this); - - addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if (isToggleEvent(e)) { - TreePath treePath = tree.getLeadSelectionPath(); - if (treePath == null) return; - final Object o = treePath.getLastPathComponent(); - if (!(o instanceof CheckedTreeNode)) return; - CheckedTreeNode firstNode = (CheckedTreeNode)o; - boolean checked = toggleNode(firstNode); - - TreePath[] selectionPaths = tree.getSelectionPaths(); - for (int i = 0; selectionPaths != null && i < selectionPaths.length; i++) { - final TreePath selectionPath = selectionPaths[i]; - final Object o1 = selectionPath.getLastPathComponent(); - if (!(o1 instanceof CheckedTreeNode)) continue; - CheckedTreeNode node = (CheckedTreeNode)o1; - checkNode(node, checked); - ((DefaultTreeModel)tree.getModel()).nodeChanged(node); - } - - e.consume(); - } - } - }); - - tree.setSelectionRow(0); - } - - private static boolean isToggleEvent(KeyEvent e) { - return e.getKeyCode() == KeyEvent.VK_SPACE; - } - - protected boolean toggleNode(CheckedTreeNode node) { - boolean checked = !node.isChecked(); - checkNode(node, checked); - - // notify model listeners about model change - final TreeModel model = getTree().getModel(); - model.valueForPathChanged(new TreePath(node.getPath()), node.getUserObject()); - - return checked; - } - - private void checkNode(CheckedTreeNode node, boolean checked) { - adjustParentsAndChildren(node, checked); - repaint(); - } - - private void adjustParentsAndChildren(final CheckedTreeNode node, final boolean checked) { - changeNodeState(node, checked); - if (!checked) { - TreeNode parent = node.getParent(); - while (parent != null) { - if (parent instanceof CheckedTreeNode) { - changeNodeState((CheckedTreeNode)parent, false); - } - parent = parent.getParent(); - } - uncheckChildren(node); - } - else { - checkChildren(node); - } - repaint(); - } - - private static void changeNodeState(final CheckedTreeNode node, final boolean checked) { - if (node.isChecked() != checked) { - node.setChecked(checked); - } - } - - private static void uncheckChildren(final CheckedTreeNode node) { - final Enumeration children = node.children(); - while (children.hasMoreElements()) { - final Object o = children.nextElement(); - if (!(o instanceof CheckedTreeNode)) continue; - CheckedTreeNode child = (CheckedTreeNode)o; - changeNodeState(child, false); - uncheckChildren(child); - } - } - - private static void checkChildren(final CheckedTreeNode node) { - final Enumeration children = node.children(); - while (children.hasMoreElements()) { - final Object o = children.nextElement(); - if (!(o instanceof CheckedTreeNode)) continue; - CheckedTreeNode child = (CheckedTreeNode)o; - changeNodeState(child, true); - checkChildren(child); - } - } - - @SuppressWarnings("unchecked") - public <T> T[] getCheckedNodes(final Class<T> nodeType) { - final ArrayList<T> nodes = new ArrayList<T>(); - final Object root = getTree().getModel().getRoot(); - if (!(root instanceof CheckedTreeNode)) { - throw new IllegalStateException("The root must be instance of the " + CheckedTreeNode.class.getName() + ": " + root.getClass().getName()); - } - new Object() { - @SuppressWarnings("unchecked") - public void collect(CheckedTreeNode node) { - if (node.isLeaf()) { - Object userObject = node.getUserObject(); - if (node.isChecked() && userObject != null && nodeType.isAssignableFrom(userObject.getClass())) { - final T value = (T)userObject; - nodes.add(value); - } - } - else { - for (int i = 0; i < node.getChildCount(); i++) { - final TreeNode child = node.getChildAt(i); - if (child instanceof CheckedTreeNode) { - collect((CheckedTreeNode)child); - } - } - } - } - }.collect((CheckedTreeNode)root); - T[] result = (T[])Array.newInstance(nodeType, nodes.size()); - nodes.toArray(result); - return result; - } -} diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/DetectedRootsChooserDialog.java b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/DetectedRootsChooserDialog.java index ee44bdf2f08b..bf3d383aeebb 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/DetectedRootsChooserDialog.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/DetectedRootsChooserDialog.java @@ -26,11 +26,13 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.*; import com.intellij.ui.treeStructure.treetable.TreeColumnInfo; import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.Convertor; import com.intellij.util.ui.ColumnInfo; import com.intellij.util.ui.ComboBoxCellEditor; import com.intellij.util.ui.tree.TreeUtil; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -38,12 +40,11 @@ import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; +import javax.swing.tree.TreePath; import java.awt.*; import java.io.File; -import java.util.Arrays; -import java.util.HashMap; +import java.util.*; import java.util.List; -import java.util.Map; /** * This dialog allows selecting paths inside selected archives or directories. @@ -108,17 +109,17 @@ public class DetectedRootsChooserDialog extends DialogWrapper { private JScrollPane myPane; private String myDescription; - public DetectedRootsChooserDialog(Component component, List<SuggestedChildRootInfo> suggestedRoots) { + public DetectedRootsChooserDialog(Component component, Collection<SuggestedChildRootInfo> suggestedRoots) { super(component, true); init(suggestedRoots); } - public DetectedRootsChooserDialog(Project project, List<SuggestedChildRootInfo> suggestedRoots) { + public DetectedRootsChooserDialog(Project project, Collection<SuggestedChildRootInfo> suggestedRoots) { super(project, true); init(suggestedRoots); } - private void init(List<SuggestedChildRootInfo> suggestedRoots) { + private void init(Collection<SuggestedChildRootInfo> suggestedRoots) { myDescription = XmlStringUtil.wrapInHtml(ApplicationNamesInfo.getInstance().getFullProductName() + " just scanned files and detected the following " + StringUtil.pluralize("root", suggestedRoots.size()) + ".<br>" + "Select items in the tree below or press Cancel to cancel operation."); @@ -128,7 +129,7 @@ public class DetectedRootsChooserDialog extends DialogWrapper { init(); } - private static CheckboxTreeTable createTreeTable(List<SuggestedChildRootInfo> suggestedRoots) { + private static CheckboxTreeTable createTreeTable(Collection<SuggestedChildRootInfo> suggestedRoots) { final CheckedTreeNode root = createRoot(suggestedRoots); CheckboxTreeTable treeTable = new CheckboxTreeTable(root, new CheckboxTree.CheckboxTreeCellRenderer(true) { @Override @@ -189,14 +190,30 @@ public class DetectedRootsChooserDialog extends DialogWrapper { column.setPreferredWidth(width); column.setMaxWidth(width); treeTable.setRootVisible(false); + new TreeTableSpeedSearch(treeTable, new Convertor<TreePath, String>() { + @Override + public String convert(TreePath o) { + Object node = o.getLastPathComponent(); + if (!(node instanceof VirtualFileCheckedTreeNode)) return ""; + return ((VirtualFileCheckedTreeNode)node).getFile().getPresentableUrl(); + } + }); TreeUtil.expandAll(treeTable.getTree()); return treeTable; } - private static CheckedTreeNode createRoot(List<SuggestedChildRootInfo> suggestedRoots) { + private static CheckedTreeNode createRoot(Collection<SuggestedChildRootInfo> suggestedRoots) { + SuggestedChildRootInfo[] sortedRoots = suggestedRoots.toArray(new SuggestedChildRootInfo[suggestedRoots.size()]); + Arrays.sort(sortedRoots, new Comparator<SuggestedChildRootInfo>() { + @Override + public int compare(@NotNull SuggestedChildRootInfo o1, @NotNull SuggestedChildRootInfo o2) { + return o1.getDetectedRoot().getFile().getPresentableUrl().compareTo(o2.getDetectedRoot().getFile().getPresentableUrl()); + } + }); + CheckedTreeNode root = new CheckedTreeNode(null); Map<VirtualFile, CheckedTreeNode> rootCandidateNodes = new HashMap<VirtualFile, CheckedTreeNode>(); - for (SuggestedChildRootInfo rootInfo : suggestedRoots) { + for (SuggestedChildRootInfo rootInfo : sortedRoots) { final VirtualFile rootCandidate = rootInfo.getRootCandidate(); CheckedTreeNode parent = rootCandidateNodes.get(rootCandidate); if (parent == null) { @@ -230,6 +247,12 @@ public class DetectedRootsChooserDialog extends DialogWrapper { return "DetectedRootsChooserDialog"; } + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myTreeTable; + } + private static class VirtualFileCheckedTreeNode extends CheckedTreeNode { private final VirtualFile myFile; diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/SuggestedChildRootInfo.java b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/SuggestedChildRootInfo.java index 743e408b8c97..725216600ffa 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/SuggestedChildRootInfo.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/SuggestedChildRootInfo.java @@ -19,6 +19,7 @@ import com.intellij.openapi.roots.libraries.LibraryRootType; import com.intellij.openapi.roots.libraries.ui.DetectedLibraryRoot; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.Map; @@ -32,17 +33,19 @@ class SuggestedChildRootInfo { private final Map<LibraryRootType, String> myRootTypeNames; private LibraryRootType mySelectedRootType; - SuggestedChildRootInfo(VirtualFile rootCandidate, DetectedLibraryRoot detectedRoot, Map<LibraryRootType, String> rootTypeNames) { + SuggestedChildRootInfo(@NotNull VirtualFile rootCandidate, @NotNull DetectedLibraryRoot detectedRoot, @NotNull Map<LibraryRootType, String> rootTypeNames) { myRootCandidate = rootCandidate; myDetectedRoot = detectedRoot; myRootTypeNames = rootTypeNames; mySelectedRootType = detectedRoot.getTypes().get(0); } + @NotNull public VirtualFile getRootCandidate() { return myRootCandidate; } + @NotNull public DetectedLibraryRoot getDetectedRoot() { return myDetectedRoot; } @@ -51,6 +54,7 @@ class SuggestedChildRootInfo { return myRootTypeNames.get(type); } + @NotNull public LibraryRootType getSelectedRootType() { return mySelectedRootType; } @@ -64,6 +68,7 @@ class SuggestedChildRootInfo { } } + @NotNull public String[] getRootTypeNames() { final String[] types = ArrayUtil.toStringArray(myRootTypeNames.values()); Arrays.sort(types, String.CASE_INSENSITIVE_ORDER); diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/CommonContentEntriesEditor.java b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/CommonContentEntriesEditor.java index 72f9c5a965c0..04f908a17c00 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/CommonContentEntriesEditor.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/CommonContentEntriesEditor.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. @@ -33,12 +33,15 @@ import com.intellij.openapi.roots.ModuleRootModel; import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel; import com.intellij.openapi.roots.ui.componentsList.layout.VerticalStackLayout; import com.intellij.openapi.roots.ui.configuration.actions.IconWithTextAction; -import com.intellij.openapi.ui.Splitter; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.ex.VirtualFileManagerAdapter; +import com.intellij.ui.JBSplitter; +import com.intellij.ui.OnePixelSplitter; import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.border.CustomLineBorder; import com.intellij.ui.roots.ToolbarPanel; import com.intellij.util.Consumer; import com.intellij.util.ui.UIUtil; @@ -48,6 +51,7 @@ import org.jetbrains.jps.model.module.JpsModuleSourceRootType; import javax.swing.*; import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; @@ -146,7 +150,9 @@ public class CommonContentEntriesEditor extends ModuleElementsEditor { myContentEntryEditorListener = new MyContentEntryEditorListener(); final JPanel mainPanel = new JPanel(new BorderLayout()); - mainPanel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6)); + if (!Registry.is("ide.new.project.settings")) { + mainPanel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6)); + } addAdditionalSettingsToPanel(mainPanel); @@ -159,21 +165,32 @@ public class CommonContentEntriesEditor extends ModuleElementsEditor { myEditorsPanel = new ScrollablePanel(new VerticalStackLayout()); myEditorsPanel.setBackground(BACKGROUND_COLOR); - JScrollPane myScrollPane = ScrollPaneFactory.createScrollPane(myEditorsPanel); - entriesPanel.add(new ToolbarPanel(myScrollPane, group), BorderLayout.CENTER); + JScrollPane myScrollPane = ScrollPaneFactory.createScrollPane(myEditorsPanel, Registry.is("ide.new.project.settings")); + final ToolbarPanel toolbarPanel = new ToolbarPanel(myScrollPane, group); + if (Registry.is("ide.new.project.settings")) { + toolbarPanel.setBorder(new CustomLineBorder(1,0,0,0)); + } + entriesPanel.add(toolbarPanel, BorderLayout.CENTER); - final Splitter splitter = new Splitter(false); + final JBSplitter splitter = Registry.is("ide.new.project.settings") ? new OnePixelSplitter(false) : new JBSplitter(false); splitter.setProportion(0.6f); splitter.setHonorComponentsMinimumSize(true); myRootTreeEditor = createContentEntryTreeEditor(project); - splitter.setFirstComponent(myRootTreeEditor.createComponent()); + final JComponent component = myRootTreeEditor.createComponent(); + if (Registry.is("ide.new.project.settings")) { + component.setBorder(new CustomLineBorder(1,0,0,0)); + } + + splitter.setFirstComponent(component); splitter.setSecondComponent(entriesPanel); JPanel contentPanel = new JPanel(new GridBagLayout()); - contentPanel.setBorder(BorderFactory.createEtchedBorder()); + if (!Registry.is("ide.new.project.settings")) { + contentPanel.setBorder(BorderFactory.createEtchedBorder()); + } final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, myRootTreeEditor.getEditingActionsGroup(), true); contentPanel.add(new JLabel("Mark as:"), - new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, 0, new Insets(0, 5, 0, 5), 0, 0)); + new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, 0, new Insets(0, 10, 0, 10), 0, 0)); contentPanel.add(actionToolbar.getComponent(), new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); @@ -236,7 +253,11 @@ public class CommonContentEntriesEditor extends ModuleElementsEditor { if (componentBorder != null) { border = BorderFactory.createCompoundBorder(border, componentBorder); } - component.setBorder(border); + if (Registry.is("ide.new.project.settings")) { + component.setBorder(new EmptyBorder(0,0,0,0)); + } else { + component.setBorder(border); + } myEditorsPanel.add(component); } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ContentRootPanel.java b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ContentRootPanel.java index 469d966a29d8..bc10302c9c43 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ContentRootPanel.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ContentRootPanel.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. @@ -36,6 +36,7 @@ import com.intellij.ui.roots.IconActionComponent; import com.intellij.ui.roots.ResizingWrapper; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; +import com.intellij.util.NotNullProducer; import com.intellij.util.containers.MultiMap; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -59,10 +60,22 @@ import java.util.Map; */ public abstract class ContentRootPanel extends JPanel { private static final Color EXCLUDED_COLOR = new JBColor(new Color(0x992E00), DarculaColors.RED); - private static final Color SELECTED_HEADER_COLOR = new JBColor(new Color(0xDEF2FF), UIUtil.getPanelBackground().darker()); + private static final Color SELECTED_HEADER_COLOR = new JBColor(new NotNullProducer<Color>() { + @NotNull + @Override + public Color produce() { + return UIUtil.isUnderDarcula() ? UIUtil.getPanelBackground().darker() : new Color(0xDEF2FF); + } + }); private static final Color HEADER_COLOR = new JBColor(new Color(0xF5F5F5), Gray._82); private static final Color SELECTED_CONTENT_COLOR = new Color(0xF0F9FF); - private static final Color CONTENT_COLOR = new JBColor(Color.WHITE, UIUtil.getPanelBackground()); + private static final Color CONTENT_COLOR = new JBColor(new NotNullProducer<Color>() { + @NotNull + @Override + public Color produce() { + return UIUtil.isUnderDarcula() ? UIUtil.getPanelBackground() : Gray._255; + } + }); private static final Color UNSELECTED_TEXT_COLOR = Gray._51; protected final ActionCallback myCallback; @@ -140,7 +153,7 @@ public abstract class ContentRootPanel extends JPanel { headerLabel.setFont(headerLabel.getFont().deriveFont(Font.BOLD)); headerLabel.setOpaque(false); if (getContentEntry().getFile() == null) { - headerLabel.setForeground(Color.RED); + headerLabel.setForeground(JBColor.RED); } final IconActionComponent deleteIconComponent = new IconActionComponent(AllIcons.Modules.DeleteContentRoot, AllIcons.Modules.DeleteContentRootRollover, diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/AddScopeUtil.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/AddScopeUtil.java deleted file mode 100644 index d92445254f28..000000000000 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/AddScopeUtil.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * User: anna - * Date: 14-May-2009 - */ -package com.intellij.profile.codeInspection.ui; - -import com.intellij.codeHighlighting.HighlightDisplayLevel; -import com.intellij.codeInspection.ex.Descriptor; -import com.intellij.codeInspection.ex.InspectionProfileImpl; -import com.intellij.codeInspection.ex.InspectionToolWrapper; -import com.intellij.codeInspection.ex.ScopeToolState; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionConfigTreeNode; -import com.intellij.psi.search.scope.packageSet.CustomScopesProviderEx; -import com.intellij.psi.search.scope.packageSet.NamedScope; -import com.intellij.psi.search.scope.packageSet.NamedScopesHolder; -import com.intellij.ui.treeStructure.Tree; -import com.intellij.ui.treeStructure.treetable.TreeTable; -import com.intellij.util.ArrayUtil; - -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreePath; -import java.util.*; - -public class AddScopeUtil { - public static ScopeToolState performAddScope(final TreeTable treeTable, - final Project project, - final InspectionProfileImpl inspectionProfile, - final Collection<InspectionConfigTreeNode> selectedNodes) { - final List<InspectionConfigTreeNode> nodes = new ArrayList<InspectionConfigTreeNode>(); - final List<Descriptor> descriptors = new ArrayList<Descriptor>(); - for (final InspectionConfigTreeNode node : selectedNodes) { - collect(descriptors, nodes, node); - } - - final List<String> availableScopes = getAvailableScopes(descriptors, project, inspectionProfile); - final int idx = Messages.showChooseDialog(treeTable, "Scope:", "Choose Scope", ArrayUtil.toStringArray(availableScopes), availableScopes.get(0), Messages.getQuestionIcon()); - if (idx == -1) return null; - final NamedScope chosenScope = NamedScopesHolder.getScope(project, availableScopes.get(idx)); - - ScopeToolState scopeToolState = null; - final Tree tree = treeTable.getTree(); - - for (final InspectionConfigTreeNode node : nodes) { - final Descriptor descriptor = node.getDefaultDescriptor(); - final InspectionToolWrapper toolWrapper = descriptor.getToolWrapper().createCopy(); //copy - final HighlightDisplayLevel level = inspectionProfile.getErrorLevel(descriptor.getKey(), chosenScope, project); - final boolean enabled = inspectionProfile.isToolEnabled(descriptor.getKey()); - scopeToolState = inspectionProfile.addScope(toolWrapper, chosenScope, level, enabled, project); - node.dropCache(); - ((DefaultTreeModel)tree.getModel()).reload(node); - tree.expandPath(new TreePath(node.getPath())); - } - tree.revalidate(); - return scopeToolState; - } - - private static void collect(final List<Descriptor> descriptors, - final List<InspectionConfigTreeNode> nodes, - final InspectionConfigTreeNode node) { - final ToolDescriptors currentDescriptors = node.getDescriptors(); - if (currentDescriptors != null) { - nodes.add(node); - descriptors.add(currentDescriptors.getDefaultDescriptor()); - descriptors.addAll(currentDescriptors.getNonDefaultDescriptors()); - } else if (node.getUserObject() instanceof String) { - for(int i = 0; i < node.getChildCount(); i++) { - final InspectionConfigTreeNode childNode = (InspectionConfigTreeNode)node.getChildAt(i); - collect(descriptors, nodes, childNode); - } - } - } - - private static List<String> getAvailableScopes(final List<Descriptor> descriptors, final Project project, final InspectionProfileImpl inspectionProfile) { - final ArrayList<NamedScope> scopes = new ArrayList<NamedScope>(); - for (final NamedScopesHolder holder : NamedScopesHolder.getAllNamedScopeHolders(project)) { - Collections.addAll(scopes, holder.getScopes()); - } - scopes.remove(CustomScopesProviderEx.getAllScope()); - - CustomScopesProviderEx.filterNoSettingsScopes(project, scopes); - - final Set<NamedScope> used = new HashSet<NamedScope>(); - for (final Descriptor descriptor : descriptors) { - final List<ScopeToolState> nonDefaultTools = inspectionProfile.getNonDefaultTools(descriptor.getKey().toString(), project); - if (nonDefaultTools != null) { - for (final ScopeToolState state : nonDefaultTools) { - used.add(state.getScope(project)); - } - } - } - scopes.removeAll(used); - - final List<String> availableScopes = new ArrayList<String>(); - for (final NamedScope scope : scopes) { - availableScopes.add(scope.getName()); - } - return availableScopes; - } -}
\ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/AdvancedSettingsAction.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/AdvancedSettingsAction.java new file mode 100644 index 000000000000..b0746769d83c --- /dev/null +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/AdvancedSettingsAction.java @@ -0,0 +1,209 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.profile.codeInspection.ui; + +import com.intellij.codeInspection.ex.InspectionProfileImpl; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionConfigTreeNode; +import com.intellij.ui.LayeredIcon; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.popup.list.ListPopupImpl; +import com.intellij.util.Consumer; +import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.EmptyIcon; +import com.intellij.util.ui.UIUtil; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Dmitry Batkovich + */ +public abstract class AdvancedSettingsAction extends AnAction { + private final int myCheckBoxIndent; + private Project myProject; + private InspectionConfigTreeNode myRoot; + + public AdvancedSettingsAction(final Project project, InspectionConfigTreeNode root) { + super("Advanced Settings"); + getTemplatePresentation().setIcon(AllIcons.General.Gear); + myProject = project; + myRoot = root; + myCheckBoxIndent = calculateCheckBoxIndent(); + } + + @Override + public void update(AnActionEvent e) { + super.update(e); + final InspectionProfileImpl inspectionProfile = getInspectionProfile(); + final Icon icon = AllIcons.General.Gear; + e.getPresentation().setIcon( + (inspectionProfile != null && inspectionProfile.isProfileLocked()) ? LayeredIcon.create(icon, PlatformIcons.LOCKED_ICON) : icon); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final ListPopupImpl actionGroupPopup = (ListPopupImpl)JBPopupFactory.getInstance().createListPopup( + new BaseListPopupStep<MyAction>(null, ContainerUtil.list(new MyDisableNewInspectionsAction(), new MyResetAction())) { + @Override + public PopupStep onChosen(MyAction selectedValue, boolean finalChoice) { + if (selectedValue.enabled()) { + selectedValue.actionPerformed(); + } + return FINAL_CHOICE; + } + }); + actionGroupPopup.getList().setCellRenderer(new ListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + return ((MyAction)value).createCustomComponent(isSelected); + } + }); + final Component component = e.getInputEvent().getComponent(); + actionGroupPopup.show(new RelativePoint(component, new Point(component.getWidth() - 1, 0))); + } + + private JLabel installLeftIndentToLabel(final JLabel label) { + label.setBorder(BorderFactory.createEmptyBorder(0, myCheckBoxIndent, 0, 0)); + return label; + } + + private class MyResetAction extends MyAction { + + protected MyResetAction() { + super("All your changes will be lost"); + } + + @Override + protected JComponent createBaseComponent() { + return installLeftIndentToLabel(new JLabel("Reset to Defaults Settings")); + } + + @Override + public void actionPerformed() { + final InspectionProfileImpl inspectionProfile = getInspectionProfile(); + if (inspectionProfile == null) { + return; + } + inspectionProfile.resetToBase(myProject); + postProcessModification(); + } + + @Override + protected boolean enabled() { + return myRoot.isProperSetting(); + } + } + + private class MyDisableNewInspectionsAction extends MyAction { + public MyDisableNewInspectionsAction() { + super("New inspections may appear when " + ApplicationNamesInfo.getInstance().getFullProductName() + " is updated"); + } + + @Override + protected JComponent createBaseComponent() { + final JCheckBox checkBox = new JCheckBox("Disable new inspections by default"); + final InspectionProfileImpl profile = getInspectionProfile(); + checkBox.setEnabled(profile != null); + if (profile != null) { + checkBox.setSelected(profile.isProfileLocked()); + } + checkBox.setOpaque(false); + return checkBox; + } + + @Override + public void actionPerformed() { + final InspectionProfileImpl profile = getInspectionProfile(); + if (profile != null) { + profile.lockProfile(!profile.isProfileLocked()); + } + } + + + @Override + protected boolean enabled() { + return true; + } + } + + private abstract class MyAction { + private final String myDescription; + + protected MyAction(String description) { + myDescription = description; + } + + protected abstract JComponent createBaseComponent(); + + protected abstract void actionPerformed(); + + protected abstract boolean enabled(); + + public JComponent createCustomComponent(final boolean selected) { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); + panel.add(createBaseComponent()); + panel.add(installLeftIndentToLabel(new JBLabel(myDescription, UIUtil.ComponentStyle.MINI))); + panel.setBackground(selected ? UIUtil.getListSelectionBackground() : UIUtil.getListBackground()); + panel.setForeground(selected ? UIUtil.getListSelectionForeground() : UIUtil.getListForeground()); + UIUtil.setEnabled(panel, enabled(), true); + return panel; + } + } + + protected abstract InspectionProfileImpl getInspectionProfile(); + + protected abstract void postProcessModification(); + + private static int calculateCheckBoxIndent() { + JCheckBox checkBox = new JCheckBox(); + Icon icon = checkBox.getIcon(); + int indent = 0; + if (icon == null) { + icon = UIManager.getIcon("CheckBox.icon"); + } + if (UIUtil.isUnderDarcula() || UIUtil.isUnderIntelliJLaF()) { + icon = EmptyIcon.create(20, 18); + } + if (icon != null) { + final Insets i = checkBox.getInsets(); + final Rectangle r = checkBox.getBounds(); + final Rectangle r1 = new Rectangle(); + r1.x = i.left; + r1.y = i.top; + r1.width = r.width - (i.right + r1.x); + r1.height = r.height - (i.bottom + r1.y); + final Rectangle iconRect = new Rectangle(); + SwingUtilities.layoutCompoundLabel( + checkBox, checkBox.getFontMetrics(checkBox.getFont()), checkBox.getText(), icon, + checkBox.getVerticalAlignment(), checkBox.getHorizontalAlignment(), + checkBox.getVerticalTextPosition(), checkBox.getHorizontalTextPosition(), + r1, new Rectangle(), iconRect, + checkBox.getText() == null ? 0 : checkBox.getIconTextGap()); + indent = iconRect.x; + } + return indent + checkBox.getIconTextGap(); + } +} diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/InspectionToolsConfigurable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/InspectionToolsConfigurable.java index 794945268d19..efc60c63f89d 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/InspectionToolsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/InspectionToolsConfigurable.java @@ -151,7 +151,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable imple final Set<String> levels = new HashSet<String>(); for (Object o : rootElement.getChildren("inspection_tool")) { final Element inspectElement = (Element)o; - levels.add(inspectElement.getAttributeValue("l")); + levels.add(inspectElement.getAttributeValue("level")); for (Object s : inspectElement.getChildren("scope")) { levels.add(((Element)s).getAttributeValue("level")); } @@ -532,6 +532,6 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable imple public JComponent getPreferredFocusedComponent() { final InspectionProfileImpl inspectionProfile = getSelectedObject(); assert inspectionProfile != null : configuredProfiles(); - return getProfilePanel(inspectionProfile).getTree(); + return getProfilePanel(inspectionProfile).getPreferredFocusedComponent(); } } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/LevelChooserAction.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/LevelChooserAction.java index 1067a60ed358..df3b8bda3f3b 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/LevelChooserAction.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/LevelChooserAction.java @@ -43,7 +43,11 @@ public abstract class LevelChooserAction extends ComboBoxAction { private HighlightSeverity myChosen = null; public LevelChooserAction(final InspectionProfileImpl profile) { - mySeverityRegistrar = ((SeverityProvider)profile.getProfileManager()).getOwnSeverityRegistrar(); + this(((SeverityProvider)profile.getProfileManager()).getOwnSeverityRegistrar()); + } + + public LevelChooserAction(final SeverityRegistrar severityRegistrar) { + mySeverityRegistrar = severityRegistrar; } @NotNull diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/MultiScopeSeverityIcon.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/MultiScopeSeverityIcon.java deleted file mode 100644 index 2bf909666f82..000000000000 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/MultiScopeSeverityIcon.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.profile.codeInspection.ui; - -import javax.swing.*; -import java.awt.*; -import java.util.List; - -/** - * @author Dmitry Batkovich - */ -public class MultiScopeSeverityIcon implements Icon { - private final int mySize; - private final List<Color> myColors; - - public MultiScopeSeverityIcon(final int size, final List<Color> colors) { - mySize = size; - myColors = colors; - } - - @Override - public void paintIcon(final Component c, final Graphics g, final int i, final int j) { - final int iconWidth = getIconWidth(); - final int iconHeightCoordinate = j + getIconHeight(); - - final int partWidth = iconWidth / myColors.size(); - - for (int idx = 0; idx < myColors.size(); idx++) { - final Color color = myColors.get(idx); - g.setColor(color); - final int x = i + partWidth * idx; - g.fillRect(x, j, x + partWidth, iconHeightCoordinate); - } - } - - @Override - public int getIconWidth() { - return mySize; - } - - @Override - public int getIconHeight() { - return mySize; - } -} diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopeOrderComparator.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopeOrderComparator.java new file mode 100644 index 000000000000..f2d8c5e1fd82 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopeOrderComparator.java @@ -0,0 +1,62 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.profile.codeInspection.ui; + +import com.intellij.codeInspection.ex.InspectionProfileImpl; +import com.intellij.util.ArrayUtil; + +import java.util.Comparator; + +/** + * @author Dmitry Batkovich + */ +public class ScopeOrderComparator implements Comparator<String> { + private final String[] myScopesOrder; + + public ScopeOrderComparator(final InspectionProfileImpl inspectionProfile) { + this(inspectionProfile.getScopesOrder()); + } + + public ScopeOrderComparator(String[] scopesOrder) { + myScopesOrder = scopesOrder; + } + + private int getKey(String scope) { + return myScopesOrder == null ? -1 : ArrayUtil.indexOf(myScopesOrder, scope); + } + + @Override + public int compare(String scope1, String scope2) { + final int key = getKey(scope1); + final int key1 = getKey(scope2); + if (key >= 0) { + if (key1 >= 0) { + return key - key1; + } + else { + return -1; + } + } + else { + if (key1 >= 0) { + return 1; + } + else { + return scope1.compareTo(scope2); + } + } + } +}
\ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopesChooser.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopesChooser.java index 4932e7ff5ca9..fde3b141084d 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopesChooser.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopesChooser.java @@ -22,13 +22,16 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.actionSystem.ex.ComboBoxAction; import com.intellij.openapi.project.Project; +import com.intellij.psi.search.scope.NonProjectFilesScope; import com.intellij.psi.search.scope.packageSet.CustomScopesProviderEx; import com.intellij.psi.search.scope.packageSet.NamedScope; import com.intellij.psi.search.scope.packageSet.NamedScopesHolder; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.util.ArrayList; +import java.util.Set; import java.util.List; import java.util.Collections; @@ -36,22 +39,28 @@ import java.util.Collections; * @author Dmitry Batkovich */ public abstract class ScopesChooser extends ComboBoxAction { + public static final String TITLE = "Select a scope to change its settings"; private final List<Descriptor> myDefaultDescriptors; private final InspectionProfileImpl myInspectionProfile; private final Project myProject; + private final Set<String> myExcludedScopeNames; - public ScopesChooser(final List<Descriptor> defaultDescriptors, final InspectionProfileImpl inspectionProfile, final Project project) { + public ScopesChooser(final List<Descriptor> defaultDescriptors, + final InspectionProfileImpl inspectionProfile, + final Project project, + final String[] excludedScopeNames) { myDefaultDescriptors = defaultDescriptors; myInspectionProfile = inspectionProfile; myProject = project; - setPopupTitle("Select a scope to change its settings"); + myExcludedScopeNames = excludedScopeNames == null ? Collections.<String>emptySet() : ContainerUtil.newHashSet(excludedScopeNames); + setPopupTitle(TITLE); getTemplatePresentation().setText("In All Scopes"); } @NotNull @Override - protected DefaultActionGroup createPopupActionGroup(final JComponent button) { + public DefaultActionGroup createPopupActionGroup(final JComponent component) { final DefaultActionGroup group = new DefaultActionGroup(); final List<NamedScope> predefinedScopes = new ArrayList<NamedScope>(); @@ -61,30 +70,47 @@ public abstract class ScopesChooser extends ComboBoxAction { predefinedScopes.addAll(holder.getPredefinedScopes()); } predefinedScopes.remove(CustomScopesProviderEx.getAllScope()); - fillActionGroup(group, predefinedScopes, myDefaultDescriptors, myInspectionProfile); + for (NamedScope predefinedScope : predefinedScopes) { + if (predefinedScope instanceof NonProjectFilesScope) { + predefinedScopes.remove(predefinedScope); + break; + } + } + + fillActionGroup(group, predefinedScopes, myDefaultDescriptors, myInspectionProfile, myExcludedScopeNames); group.addSeparator(); - fillActionGroup(group, customScopes, myDefaultDescriptors, myInspectionProfile); + fillActionGroup(group, customScopes, myDefaultDescriptors, myInspectionProfile, myExcludedScopeNames); - //TODO edit scopes order - //group.addSeparator(); - //group.add(new AnAction("Edit Scopes Order...") { - // @Override - // public void actionPerformed(final AnActionEvent e) { - // - // } - //}); + group.addSeparator(); + group.add(new AnAction("Edit Scopes Order...") { + @Override + public void actionPerformed(final AnActionEvent e) { + final ScopesOrderDialog dlg = new ScopesOrderDialog(component, myInspectionProfile, myProject); + dlg.show(); + if (dlg.isOK()) { + onScopesOrderChanged(); + } + } + }); return group; } + protected abstract void onScopesOrderChanged(); + protected abstract void onScopeAdded(); private void fillActionGroup(final DefaultActionGroup group, - final List<NamedScope> scopes, - final List<Descriptor> defaultDescriptors, - final InspectionProfileImpl inspectionProfile) { + final List<NamedScope> scopes, + final List<Descriptor> defaultDescriptors, + final InspectionProfileImpl inspectionProfile, + final Set<String> excludedScopeNames) { for (final NamedScope scope : scopes) { - group.add(new AnAction(scope.getName()) { + final String scopeName = scope.getName(); + if (excludedScopeNames.contains(scopeName)) { + continue; + } + group.add(new AnAction(scopeName) { @Override public void actionPerformed(final AnActionEvent e) { for (final Descriptor defaultDescriptor : defaultDescriptors) { diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopesOrderDialog.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopesOrderDialog.java new file mode 100644 index 000000000000..c69a3e5566a2 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ScopesOrderDialog.java @@ -0,0 +1,121 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.profile.codeInspection.ui; + +import com.intellij.codeInspection.ex.InspectionProfileImpl; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.psi.search.scope.NonProjectFilesScope; +import com.intellij.psi.search.scope.packageSet.CustomScopesProviderEx; +import com.intellij.psi.search.scope.packageSet.NamedScope; +import com.intellij.psi.search.scope.packageSet.NamedScopesHolder; +import com.intellij.ui.*; +import com.intellij.ui.components.JBList; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author Dmitry Batkovich + */ +public class ScopesOrderDialog extends DialogWrapper { + + private final JList myOptionsList = new JBList(); + + private final InspectionProfileImpl myInspectionProfile; + private final Project myProject; + private final JPanel myPanel; + + public ScopesOrderDialog(final @NotNull Component parent, + final InspectionProfileImpl inspectionProfile, + final Project project) { + super(parent, true); + myInspectionProfile = inspectionProfile; + myProject = project; + + final JPanel listPanel = ToolbarDecorator.createDecorator(myOptionsList).setMoveDownAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton anActionButton) { + ListUtil.moveSelectedItemsDown(myOptionsList); + } + }).setMoveUpAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton anActionButton) { + ListUtil.moveSelectedItemsUp(myOptionsList); + } + }).disableRemoveAction().disableAddAction().createPanel(); + final JLabel descr = new JLabel("<html><p>If file appears in two or more scopes, it will be" + + "inspected with settings of the topmost scope in list above.</p><p/>" + + "<p>Scope order is set globally for all inspections in the profile.</p></html>"); + descr.setPreferredSize(new Dimension(300, 100)); + UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, descr); + myPanel = new JPanel(); + myPanel.setLayout(new BorderLayout()); + myPanel.add(listPanel, BorderLayout.CENTER); + myPanel.add(descr, BorderLayout.SOUTH); + fillList(); + init(); + setTitle("Scopes Order"); + } + + private void fillList() { + DefaultListModel model = new DefaultListModel(); + model.removeAllElements(); + + final List<String> scopes = new ArrayList<String>(); + for (final NamedScopesHolder holder : NamedScopesHolder.getAllNamedScopeHolders(myProject)) { + for (final NamedScope scope : holder.getScopes()) { + if (!(scope instanceof NonProjectFilesScope)) { + scopes.add(scope.getName()); + } + } + } + scopes.remove(CustomScopesProviderEx.getAllScope().getName()); + Collections.sort(scopes, new ScopeOrderComparator(myInspectionProfile)); + for (String scopeName : scopes) { + model.addElement(scopeName); + } + myOptionsList.setModel(model); + myOptionsList.setSelectedIndex(0); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return myPanel; + } + + @Override + protected void doOKAction() { + final int size = myOptionsList.getModel().getSize(); + final String[] newScopeOrder = new String[size]; + for (int i = 0; i < size; i++) { + final String scopeName = (String) myOptionsList.getModel().getElementAt(i); + newScopeOrder[i] = scopeName; + } + if (!Arrays.equals(newScopeOrder, myInspectionProfile.getScopesOrder())) { + myInspectionProfile.setScopesOrder(newScopeOrder); + } + super.doOKAction(); + } +} diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java index ce5f308c1305..2da5c80a758b 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java @@ -132,6 +132,8 @@ public class SingleInspectionProfilePanel extends JPanel { private Splitter myRightSplitter; private Splitter myMainSplitter; + private String[] myInitialScopesOrder; + public SingleInspectionProfilePanel(@NotNull InspectionProjectProfileManager projectProfileManager, @NotNull String inspectionProfileName, @NotNull ModifiableModel profile) { @@ -186,8 +188,10 @@ public class SingleInspectionProfilePanel extends JPanel { if (myTreeTable != null) { final TreePath selectionPath = myTreeTable.getTree().getSelectionPath(); if (selectionPath != null) { - TreeUtil.selectNode(myTreeTable.getTree(), (TreeNode)selectionPath.getLastPathComponent()); - TreeUtil.showRowCentered(myTreeTable.getTree(), myTreeTable.getTree().getRowForPath(selectionPath), false); + TreeUtil.selectNode(myTreeTable.getTree(), (TreeNode) selectionPath.getLastPathComponent()); + final int rowForPath = myTreeTable.getTree().getRowForPath(selectionPath); + TableUtil.selectRows(myTreeTable, new int[]{rowForPath}); + scrollToCenter(); } } } @@ -258,6 +262,7 @@ public class SingleInspectionProfilePanel extends JPanel { if (!accept(state.getTool())) continue; myInitialToolDescriptors.add(ToolDescriptors.fromScopeToolState(state, profile, project)); } + myInitialScopesOrder = mySelectedProfile.getScopesOrder(); } protected boolean accept(InspectionToolWrapper entry) { @@ -364,24 +369,6 @@ public class SingleInspectionProfilePanel extends JPanel { actions.add(actionManager.createExpandAllAction(myTreeExpander, myTreeTable)); actions.add(actionManager.createCollapseAllAction(myTreeExpander, myTreeTable)); - - actions.add(new AnAction(CommonBundle.message("button.reset.to.default"), CommonBundle.message("button.reset.to.default"), - AllIcons.General.Reset) { - { - registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK)), myTreeTable); - } - @Override - public void update(AnActionEvent e) { - e.getPresentation().setEnabled(myRoot.isProperSetting()); - } - - @Override - public void actionPerformed(AnActionEvent e) { - mySelectedProfile.resetToBase(myProjectProfileManager.getProject()); - postProcessModification(); - } - }); - actions.add(new AnAction("Reset to Empty", "Reset to empty", AllIcons.Actions.Reset_to_empty){ @Override @@ -396,18 +383,19 @@ public class SingleInspectionProfilePanel extends JPanel { } }); - actions.add(new ToggleAction("Lock Profile", "Lock profile", AllIcons.Nodes.Padlock) { + actions.add(new AdvancedSettingsAction(myProjectProfileManager.getProject(), myRoot) { @Override - public boolean isSelected(AnActionEvent e) { - return mySelectedProfile != null && mySelectedProfile.isProfileLocked(); + protected InspectionProfileImpl getInspectionProfile() { + return mySelectedProfile; } @Override - public void setSelected(AnActionEvent e, boolean state) { - mySelectedProfile.lockProfile(state); + protected void postProcessModification() { + SingleInspectionProfilePanel.this.postProcessModification(); } }); + final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actions, true); actionToolbar.setTargetComponent(this); return actionToolbar; @@ -424,11 +412,24 @@ public class SingleInspectionProfilePanel extends JPanel { public void selectInspectionTool(String name) { final InspectionConfigTreeNode node = findNodeByKey(name, myRoot); if (node != null) { - TreeUtil.showRowCentered(myTreeTable.getTree(), myTreeTable.getTree().getRowForPath(new TreePath(node.getPath())) - 1, true);//myTree.isRootVisible ? 0 : 1; TreeUtil.selectNode(myTreeTable.getTree(), node); + final int rowForPath = myTreeTable.getTree().getRowForPath(new TreePath(node.getPath())); + TableUtil.selectRows(myTreeTable, new int[]{rowForPath}); + scrollToCenter(); } } + private void scrollToCenter() { + ListSelectionModel selectionModel = myTreeTable.getSelectionModel(); + int maxSelectionIndex = selectionModel.getMaxSelectionIndex(); + final int maxColumnSelectionIndex = Math.max(0, myTreeTable.getColumnModel().getSelectionModel().getMinSelectionIndex()); + Rectangle maxCellRect = myTreeTable.getCellRect(maxSelectionIndex, maxColumnSelectionIndex, false); + + final Point selectPoint = maxCellRect.getLocation(); + final int allHeight = myTreeTable.getVisibleRect().height; + myTreeTable.scrollRectToVisible(new Rectangle(new Point(0, Math.max(0, selectPoint.y - allHeight / 2)), new Dimension(0, allHeight))); + } + @Nullable private static InspectionConfigTreeNode findNodeByKey(String name, InspectionConfigTreeNode root) { for (int i = 0; i < root.getChildCount(); i++) { @@ -524,6 +525,7 @@ public class SingleInspectionProfilePanel extends JPanel { final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTreeTable); + myTreeTable.getTree().setShowsRootHandles(true); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); TreeUtil.collapseAll(myTreeTable.getTree(), 1); @@ -684,14 +686,15 @@ public class SingleInspectionProfilePanel extends JPanel { } private void updateOptionsAndDescriptionPanel(final TreePath... paths) { - if (paths == null || paths.length == 0) { + if (mySelectedProfile == null || paths == null || paths.length == 0) { return; } final TreePath path = paths[0]; if (path == null) return; final List<InspectionConfigTreeNode> nodes = InspectionsAggregationUtil.getInspectionsNodes(paths); if (!nodes.isEmpty()) { - final InspectionConfigTreeNode singleNode = nodes.size() == 1 ? ContainerUtil.getFirstItem(nodes) : null; + final InspectionConfigTreeNode singleNode = paths.length == 1 && ((InspectionConfigTreeNode)paths[0].getLastPathComponent()).getDefaultDescriptor() != null + ? ContainerUtil.getFirstItem(nodes) : null; if (singleNode != null && singleNode.getDefaultDescriptor().loadDescription() != null) { // need this in order to correctly load plugin-supplied descriptions final Descriptor defaultDescriptor = singleNode.getDefaultDescriptor(); @@ -721,7 +724,7 @@ public class SingleInspectionProfilePanel extends JPanel { } else { try { - myBrowser.read(new StringReader(EMPTY_HTML), null); + myBrowser.read(new StringReader("<html><body>Multiple inspections are selected. You can edit them as a single inspection.</body></html>"), null); } catch (IOException e1) { //Can't be @@ -733,7 +736,6 @@ public class SingleInspectionProfilePanel extends JPanel { final JPanel severityPanel = new JPanel(new GridBagLayout()); final double severityPanelWeightY; final JPanel configPanelAnchor = new JPanel(new GridLayout()); - configPanelAnchor.setBorder(IdeBorderFactory.createTitledBorder("Options", false, new Insets(0, 0, 0, 0))); final Set<String> scopesNames = new THashSet<String>(); for (final InspectionConfigTreeNode node : nodes) { @@ -754,7 +756,7 @@ public class SingleInspectionProfilePanel extends JPanel { final HighlightDisplayKey key = node.getDefaultDescriptor().getKey(); final NamedScope scope = node.getDefaultDescriptor().getScope(); final boolean toUpdate = mySelectedProfile.getErrorLevel(key, scope, project) != level; - mySelectedProfile.setErrorLevel(key, level, -1, project); + mySelectedProfile.setErrorLevel(key, level, null, project); if (toUpdate) node.dropCache(); } @@ -774,7 +776,13 @@ public class SingleInspectionProfilePanel extends JPanel { public Descriptor fun(final InspectionConfigTreeNode node) { return node.getDefaultDescriptor(); } - }), mySelectedProfile, project) { + }), mySelectedProfile, project, null) { + @Override + protected void onScopesOrderChanged() { + myTreeTable.getTree().updateUI(); + updateOptionsAndDescriptionPanel(); + } + @Override protected void onScopeAdded() { updateOptionsAndDescriptionPanel(); @@ -812,12 +820,19 @@ public class SingleInspectionProfilePanel extends JPanel { } @Override - protected void onChange() { + protected void onSettingsChanged() { myTreeTable.getTree().updateUI(); } @Override protected void onScopeAdded() { + updateOptionsAndDescriptionPanel(); + } + + @Override + protected void onScopesOrderChanged() { + myTreeTable.getTree().updateUI(); + updateOptionsAndDescriptionPanel(); } @Override @@ -829,8 +844,9 @@ public class SingleInspectionProfilePanel extends JPanel { }); - final ToolbarDecorator wrappedTable = ToolbarDecorator.createDecorator(scopesAndScopesAndSeveritiesTable); + final ToolbarDecorator wrappedTable = ToolbarDecorator.createDecorator(scopesAndScopesAndSeveritiesTable).disableUpDownActions(); final JPanel panel = wrappedTable.createPanel(); + panel.setMinimumSize(new Dimension(getMinimumSize().width, 3 * scopesAndScopesAndSeveritiesTable.getRowHeight())); severityPanel.add(new JBLabel("Scopes & Severities"), new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(5, 0, 2, 10), 0, 0)); @@ -842,8 +858,13 @@ public class SingleInspectionProfilePanel extends JPanel { severityPanelWeightY = 0.3; } myOptionsPanel.add(severityPanel, new GridBagConstraints(0, 0, 1, 1, 1.0, severityPanelWeightY, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); - myOptionsPanel.add(configPanelAnchor, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, - new Insets(0, 0, 0, 0), 0, 0)); + if (configPanelAnchor.getComponentCount() != 0) { + configPanelAnchor.setBorder(IdeBorderFactory.createTitledBorder("Options", false, new Insets(0, 0, 0, 0))); + } + if (configPanelAnchor.getComponentCount() != 0 || scopesNames.isEmpty()) { + myOptionsPanel.add(configPanelAnchor, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + } myOptionsPanel.revalidate(); GuiUtils.enableChildren(myOptionsPanel, isThoughOneNodeEnabled(nodes)); } @@ -887,7 +908,10 @@ public class SingleInspectionProfilePanel extends JPanel { private static void setConfigPanel(final JPanel configPanelAnchor, final ScopeToolState state) { configPanelAnchor.removeAll(); - configPanelAnchor.add(state.getAdditionalConfigPanel()); + final JComponent additionalConfigPanel = state.getAdditionalConfigPanel(); + if (additionalConfigPanel != null) { + configPanelAnchor.add(ScrollPaneFactory.createScrollPane(additionalConfigPanel, SideBorder.NONE)); + } } private static InspectionConfigTreeNode getGroupNode(InspectionConfigTreeNode root, String[] groupPath) { @@ -1001,6 +1025,7 @@ public class SingleInspectionProfilePanel extends JPanel { if (mySelectedProfile.isChanged()) return true; if (myShareProfile != (mySelectedProfile.getProfileManager() == myProjectProfileManager)) return true; if (!Comparing.strEqual(myInitialProfile, mySelectedProfile.getName())) return true; + if (!Comparing.equal(myInitialScopesOrder, mySelectedProfile.getScopesOrder())) return true; if (descriptorsAreChanged()) { return true; } @@ -1111,10 +1136,6 @@ public class SingleInspectionProfilePanel extends JPanel { return false; } - public Tree getTreeTable() { - return myTreeTable.getTree(); - } - public boolean isProfileShared() { return myShareProfile; } @@ -1176,15 +1197,15 @@ public class SingleInspectionProfilePanel extends JPanel { final boolean showOptionsAndDescriptorPanels, @NotNull HighlightDisplayLevel level) { final HighlightDisplayKey key = child.getDefaultDescriptor().getKey(); - mySelectedProfile.setErrorLevel(key, level, -1, myProjectProfileManager.getProject()); + mySelectedProfile.setErrorLevel(key, level, null, myProjectProfileManager.getProject()); child.dropCache(); if (showOptionsAndDescriptorPanels) { updateOptionsAndDescriptionPanel(new TreePath(child.getPath())); } } - public JComponent getTree() { - return myTreeTable.getTree(); + public JComponent getPreferredFocusedComponent() { + return myTreeTable; } private class MyFilterComponent extends FilterComponent { diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionFilterAction.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionFilterAction.java index cbd1c2482032..bccd2db28e21 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionFilterAction.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionFilterAction.java @@ -20,7 +20,6 @@ import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; import com.intellij.codeInspection.ex.InspectionProfileImpl; import com.intellij.icons.AllIcons; import com.intellij.lang.annotation.HighlightSeverity; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.actionSystem.ex.CheckboxAction; @@ -60,6 +59,23 @@ public class InspectionFilterAction extends DefaultActionGroup { addSeparator(); add(new ShowAvailableOnlyOnAnalyzeInspectionsAction()); + add(new ShowOnlyCleanupInspectionsAction()); + } + + private class ShowOnlyCleanupInspectionsAction extends CheckboxAction { + public ShowOnlyCleanupInspectionsAction() { + super("Show Only Cleanup Inspections"); + } + + @Override + public boolean isSelected(final AnActionEvent e) { + return myInspectionsFilter.isShowOnlyCleanupInspections(); + } + + @Override + public void setSelected(final AnActionEvent e, final boolean state) { + myInspectionsFilter.setShowOnlyCleanupInspections(state); + } } private class ShowAvailableOnlyOnAnalyzeInspectionsAction extends CheckboxAction { diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionsFilter.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionsFilter.java index 432ef560ba58..117cd774dff8 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionsFilter.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionsFilter.java @@ -33,11 +33,16 @@ public abstract class InspectionsFilter { private final Set<HighlightSeverity> mySuitableSeverities = new HashSet<HighlightSeverity>(); private Boolean mySuitableInspectionsStates; private boolean myAvailableOnlyForAnalyze; + private boolean myShowOnlyCleanupInspections; public boolean isAvailableOnlyForAnalyze() { return myAvailableOnlyForAnalyze; } + public boolean isShowOnlyCleanupInspections() { + return myShowOnlyCleanupInspections; + } + public Boolean getSuitableInspectionsStates() { return mySuitableInspectionsStates; } @@ -46,6 +51,11 @@ public abstract class InspectionsFilter { return mySuitableSeverities.contains(severity); } + public void setShowOnlyCleanupInspections(final boolean showOnlyCleanupInspections) { + myShowOnlyCleanupInspections = showOnlyCleanupInspections; + filterChanged(); + } + public void setAvailableOnlyForAnalyze(final boolean availableOnlyForAnalyze) { myAvailableOnlyForAnalyze = availableOnlyForAnalyze; filterChanged(); @@ -67,10 +77,17 @@ public abstract class InspectionsFilter { } public boolean isEmptyFilter() { - return mySuitableInspectionsStates == null && !myAvailableOnlyForAnalyze && mySuitableSeverities.isEmpty(); + return mySuitableInspectionsStates == null + && !myAvailableOnlyForAnalyze + && !myShowOnlyCleanupInspections + && mySuitableSeverities.isEmpty(); } public boolean matches(final Tools tools) { + if (myShowOnlyCleanupInspections && !tools.getTool().isCleanupTool()) { + return false; + } + if (mySuitableInspectionsStates != null && mySuitableInspectionsStates != tools.isEnabled()) { return false; } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java index 966a456e1d38..92cb62f0f800 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java @@ -28,17 +28,21 @@ import com.intellij.openapi.util.Comparing; import com.intellij.profile.codeInspection.ui.InspectionsAggregationUtil; import com.intellij.profile.codeInspection.ui.table.ScopesAndSeveritiesTable; import com.intellij.profile.codeInspection.ui.table.ThreeStateCheckBoxRenderer; +import com.intellij.ui.DoubleClickListener; import com.intellij.ui.treeStructure.treetable.TreeTable; import com.intellij.ui.treeStructure.treetable.TreeTableModel; +import com.intellij.ui.treeStructure.treetable.TreeTableTree; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; +import java.awt.event.*; import java.util.*; import java.util.List; @@ -60,8 +64,8 @@ public class InspectionsConfigTreeTable extends TreeTable { final TableColumn isEnabledColumn = getColumnModel().getColumn(IS_ENABLED_COLUMN); isEnabledColumn.setMaxWidth(20); - isEnabledColumn.setCellRenderer(new ThreeStateCheckBoxRenderer()); - isEnabledColumn.setCellEditor(new ThreeStateCheckBoxRenderer()); + isEnabledColumn.setCellRenderer(new ThreeStateCheckBoxRenderer(false)); + isEnabledColumn.setCellEditor(new ThreeStateCheckBoxRenderer(true)); addMouseMotionListener(new MouseAdapter() { @Override @@ -76,10 +80,42 @@ public class InspectionsConfigTreeTable extends TreeTable { if (maybeIcon instanceof MultiScopeSeverityIcon) { final LinkedHashMap<String, HighlightSeverity> scopeToAverageSeverityMap = ((MultiScopeSeverityIcon)maybeIcon).getScopeToAverageSeverityMap(); - IdeTooltipManager.getInstance().show(new IdeTooltip(InspectionsConfigTreeTable.this, point, new ScopesAndSeveritiesHintTable(scopeToAverageSeverityMap)), false); + IdeTooltipManager.getInstance().show( + new IdeTooltip(InspectionsConfigTreeTable.this, point, new ScopesAndSeveritiesHintTable(scopeToAverageSeverityMap)), false); } } }); + + new DoubleClickListener() { + @Override + protected boolean onDoubleClick(MouseEvent event) { + final TreePath path = getTree().getPathForRow(getTree().getLeadSelectionRow()); + if (path != null) { + final InspectionConfigTreeNode node = (InspectionConfigTreeNode)path.getLastPathComponent(); + if (node.isLeaf()) { + swapInspectionEnableState(); + } + } + return true; + } + }.installOn(this); + + registerKeyboardAction(new ActionListener() { + public void actionPerformed(ActionEvent e) { + swapInspectionEnableState(); + updateUI(); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), JComponent.WHEN_FOCUSED); + + getEmptyText().setText("No enabled inspections available"); + } + + private void swapInspectionEnableState() { + for (int selectedRow : getSelectedRows()) { + final Object value = getValueAt(selectedRow, IS_ENABLED_COLUMN); + final boolean newValue = !Boolean.TRUE.equals(value); + setValueAt(newValue, selectedRow, IS_ENABLED_COLUMN); + } } public abstract static class InspectionsConfigTreeTableSettings { @@ -107,6 +143,7 @@ public class InspectionsConfigTreeTable extends TreeTable { private static class InspectionsConfigTreeTableModel extends DefaultTreeModel implements TreeTableModel { private final InspectionsConfigTreeTableSettings mySettings; + private TreeTable myTreeTable; public InspectionsConfigTreeTableModel(final InspectionsConfigTreeTableSettings settings) { super(settings.getRoot()); @@ -154,7 +191,7 @@ public class InspectionsConfigTreeTable extends TreeTable { mySettings.getInspectionProfile().getNonDefaultTools(toolId, mySettings.getProject())); } } - return sink.constructIcon(); + return sink.constructIcon(mySettings.getInspectionProfile()); } else if (column == IS_ENABLED_COLUMN) { return isEnabled(inspectionsKeys); } @@ -195,26 +232,34 @@ public class InspectionsConfigTreeTable extends TreeTable { aNode.dropCache(); mySettings.onChanged(aNode); } + if (myTreeTable != null) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + public void run() { + ((AbstractTableModel)myTreeTable.getModel()).fireTableDataChanged(); + } + }); + } } @Override public void setTree(final JTree tree) { + myTreeTable = ((TreeTableTree)tree).getTreeTable(); } } private static class MultiColoredHighlightSeverityIconSink { - private final LinkedHashMap<String, HighlightSeverity> myScopeToAverageSeverityMap = new LinkedHashMap<String, HighlightSeverity>(); + private final Map<String, HighlightSeverity> myScopeToAverageSeverityMap = new HashMap<String, HighlightSeverity>(); + private String myDefaultScopeName; private boolean myIsFirst = true; - public Icon constructIcon() { + public Icon constructIcon(final InspectionProfileImpl inspectionProfile) { if (myScopeToAverageSeverityMap.isEmpty()) { return null; } - //TODO order scopes return !allScopesHasMixedSeverity() - ? new MultiScopeSeverityIcon(myScopeToAverageSeverityMap) + ? new MultiScopeSeverityIcon(myScopeToAverageSeverityMap, myDefaultScopeName, inspectionProfile) : ScopesAndSeveritiesTable.MIXED_FAKE_LEVEL.getIcon(); } @@ -229,6 +274,9 @@ public class InspectionsConfigTreeTable extends TreeTable { public void put(final ScopeToolState defaultState, final Collection<ScopeToolState> nonDefault) { putOne(defaultState); + if (myDefaultScopeName == null) { + myDefaultScopeName = defaultState.getScopeName(); + } for (final ScopeToolState scopeToolState : nonDefault) { putOne(scopeToolState); } @@ -237,7 +285,7 @@ public class InspectionsConfigTreeTable extends TreeTable { } } - public void putOne(final ScopeToolState state) { + private void putOne(final ScopeToolState state) { final Icon icon = state.getLevel().getIcon(); final String scopeName = state.getScopeName(); if (icon instanceof HighlightDisplayLevel.SingleColorIconWithMask) { diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java index 6f093ca66349..099dbb622261 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java @@ -17,13 +17,15 @@ package com.intellij.profile.codeInspection.ui.inspectionsTree; import com.intellij.codeHighlighting.HighlightDisplayLevel; +import com.intellij.codeInspection.ex.InspectionProfileImpl; import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.profile.codeInspection.ui.ScopeOrderComparator; import com.intellij.ui.JBColor; import javax.swing.*; import java.awt.*; -import java.util.Collection; -import java.util.LinkedHashMap; +import java.util.*; +import java.util.List; /** * @author Dmitry Batkovich @@ -35,8 +37,17 @@ public class MultiScopeSeverityIcon implements Icon { private final LinkedHashMap<String, HighlightSeverity> myScopeToAverageSeverityMap; - public MultiScopeSeverityIcon(final LinkedHashMap<String, HighlightSeverity> scopeToAverageSeverityMap) { - myScopeToAverageSeverityMap = scopeToAverageSeverityMap; + public MultiScopeSeverityIcon(final Map<String, HighlightSeverity> scopeToAverageSeverityMap, + final String defaultScopeName, + final InspectionProfileImpl inspectionProfile) { + final List<String> sortedScopeNames = new ArrayList<String>(scopeToAverageSeverityMap.keySet()); + myScopeToAverageSeverityMap = new LinkedHashMap<String, HighlightSeverity>(); + Collections.sort(sortedScopeNames, new ScopeOrderComparator(inspectionProfile)); + sortedScopeNames.remove(defaultScopeName); + sortedScopeNames.add(defaultScopeName); + for (final String scopeName : sortedScopeNames) { + myScopeToAverageSeverityMap.put(scopeName, scopeToAverageSeverityMap.get(scopeName)); + } } public LinkedHashMap<String, HighlightSeverity> getScopeToAverageSeverityMap() { diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/ScopesAndSeveritiesHintTable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/ScopesAndSeveritiesHintTable.java index 05cb7ab193f5..68c7f20119bd 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/ScopesAndSeveritiesHintTable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/ScopesAndSeveritiesHintTable.java @@ -19,10 +19,12 @@ import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.profile.codeInspection.ui.SingleInspectionProfilePanel; import com.intellij.ui.table.JBTable; +import com.intellij.util.ui.UIUtil; import javax.swing.*; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableColumn; import java.awt.*; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -38,9 +40,20 @@ public class ScopesAndSeveritiesHintTable extends JBTable { public ScopesAndSeveritiesHintTable(final LinkedHashMap<String, HighlightSeverity> scopeToAverageSeverityMap) { super(new MyModel(scopeToAverageSeverityMap)); - final DefaultTableCellRenderer cellRenderer = new DefaultTableCellRenderer(); - cellRenderer.setOpaque(false); - getColumnModel().getColumn(SCOPE_COLUMN).setCellRenderer(cellRenderer); + getColumnModel().getColumn(SCOPE_COLUMN).setCellRenderer(new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setOpaque(false); + UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, this); + return this; + } + }); getColumnModel().getColumn(SEVERITY_COLUMN).setCellRenderer(new DefaultTableCellRenderer() { @Override @@ -55,6 +68,7 @@ public class ScopesAndSeveritiesHintTable extends JBTable { setIcon(HighlightDisplayLevel.find(severity).getIcon()); setText(SingleInspectionProfilePanel.renderSeverity(severity)); setOpaque(false); + UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, this); return this; } }); @@ -62,6 +76,16 @@ public class ScopesAndSeveritiesHintTable extends JBTable { setRowSelectionAllowed(false); setColumnSelectionAllowed(false); setOpaque(false); + + for (int i = 0; i < getColumnModel().getColumnCount(); i++) { + int w = 0; + final TableColumn column = getColumnModel().getColumn(i); + for (int j = 0; j < getModel().getRowCount(); j++) { + final Component component = prepareRenderer(column.getCellRenderer(), j, i); + w = Math.max(component.getPreferredSize().width, w); + } + column.setPreferredWidth(w); + } } private final static class MyModel extends AbstractTableModel { @@ -96,7 +120,7 @@ public class ScopesAndSeveritiesHintTable extends JBTable { @Override public Object getValueAt(final int rowIndex, final int columnIndex) { switch (columnIndex) { - case SCOPE_COLUMN: return myScopes.get(rowIndex); + case SCOPE_COLUMN: return rowIndex < getRowCount() - 1 ? myScopes.get(rowIndex) : "Everywhere else"; case SEVERITY_COLUMN: return myScopeToAverageSeverityMap.get(myScopes.get(rowIndex)); default: throw new IllegalArgumentException(); } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ScopesAndSeveritiesTable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ScopesAndSeveritiesTable.java index ad7dc946fff8..3b2c36bf7d34 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ScopesAndSeveritiesTable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ScopesAndSeveritiesTable.java @@ -17,20 +17,30 @@ package com.intellij.profile.codeInspection.ui.table; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInspection.ex.Descriptor; import com.intellij.codeInspection.ex.InspectionProfileImpl; import com.intellij.codeInspection.ex.ScopeToolState; import com.intellij.icons.AllIcons; +import com.intellij.ide.DataManager; import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Comparing; -import com.intellij.profile.codeInspection.ui.AddScopeUtil; +import com.intellij.profile.codeInspection.ui.ScopeOrderComparator; +import com.intellij.profile.codeInspection.ui.ScopesChooser; import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionConfigTreeNode; import com.intellij.psi.search.scope.packageSet.NamedScope; +import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.table.JBTable; import com.intellij.ui.treeStructure.treetable.TreeTable; import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.EditableModel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,8 +51,8 @@ import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; -import java.util.ArrayList; -import java.util.LinkedHashSet; +import java.awt.*; +import java.util.*; import java.util.List; /** @@ -66,8 +76,8 @@ public class ScopesAndSeveritiesTable extends JBTable { final TableColumn scopeEnabledColumn = columnModel.getColumn(SCOPE_ENABLED_COLUMN); scopeEnabledColumn.setMaxWidth(30); - scopeEnabledColumn.setCellRenderer(new ThreeStateCheckBoxRenderer()); - scopeEnabledColumn.setCellEditor(new ThreeStateCheckBoxRenderer()); + scopeEnabledColumn.setCellRenderer(new ThreeStateCheckBoxRenderer(false)); + scopeEnabledColumn.setCellEditor(new ThreeStateCheckBoxRenderer(true)); final TableColumn severityColumn = columnModel.getColumn(SEVERITY_COLUMN); severityColumn.setCellRenderer(SeverityRenderer.create(tableSettings.getInspectionProfile())); @@ -94,6 +104,8 @@ public class ScopesAndSeveritiesTable extends JBTable { setStriped(true); setShowGrid(false); + + ((MyTableModel)getModel()).setTable(this); } public abstract static class TableSettings { @@ -148,11 +160,13 @@ public class ScopesAndSeveritiesTable extends JBTable { protected abstract void onScopeAdded(); + protected abstract void onScopesOrderChanged(); + protected abstract void onScopeRemoved(final int scopesCount); protected abstract void onScopeChosen(final @NotNull ScopeToolState scopeToolState); - protected abstract void onChange(); + protected abstract void onSettingsChanged(); } @NotNull @@ -177,7 +191,9 @@ public class ScopesAndSeveritiesTable extends JBTable { private final Project myProject; private final TableSettings myTableSettings; private final List<HighlightDisplayKey> myKeys; + private final Comparator<String> myScopeComparator; + private JTable myTable; private String[] myScopeNames; public MyTableModel(final TableSettings tableSettings) { @@ -188,12 +204,24 @@ public class ScopesAndSeveritiesTable extends JBTable { myKeyNames = tableSettings.getKeyNames(); myNodes = tableSettings.getNodes(); myTreeTable = tableSettings.getTreeTable(); + myScopeComparator = new ScopeOrderComparator(myInspectionProfile); refreshAggregatedScopes(); } + public void setTable(JTable table) { + myTable = table; + } + @Override public boolean isCellEditable(final int rowIndex, final int columnIndex) { - return columnIndex != SCOPE_NAME_COLUMN; + if (columnIndex == SCOPE_NAME_COLUMN) { + return false; + } else if (columnIndex == SCOPE_ENABLED_COLUMN) { + return true; + } + assert columnIndex == SEVERITY_COLUMN; + final ExistedScopesStatesAndNonExistNames scopeToolState = getScopeToolState(rowIndex); + return scopeToolState.getNonExistNames().isEmpty(); } @Override @@ -221,7 +249,7 @@ public class ScopesAndSeveritiesTable extends JBTable { return String.class; } if (SEVERITY_COLUMN == columnIndex) { - return HighlightSeverity.class; + return SeverityState.class; } throw new IllegalArgumentException(); } @@ -235,9 +263,9 @@ public class ScopesAndSeveritiesTable extends JBTable { case SCOPE_ENABLED_COLUMN: return isEnabled(rowIndex); case SCOPE_NAME_COLUMN: - return getScope(rowIndex).getName(); + return rowIndex == lastRowIndex() ? "Everywhere else" : getScope(rowIndex).getName(); case SEVERITY_COLUMN: - return getSeverity(rowIndex); + return getSeverityState(rowIndex); default: throw new IllegalArgumentException("Invalid column index " + columnIndex); } @@ -248,12 +276,12 @@ public class ScopesAndSeveritiesTable extends JBTable { } @NotNull - private HighlightSeverity getSeverity(final int rowIndex) { + private SeverityState getSeverityState(final int rowIndex) { final ExistedScopesStatesAndNonExistNames existedScopesStatesAndNonExistNames = getScopeToolState(rowIndex); if (!existedScopesStatesAndNonExistNames.getNonExistNames().isEmpty()) { - return MIXED_FAKE_SEVERITY; + return new SeverityState(MIXED_FAKE_SEVERITY, false); } - return ScopesAndSeveritiesTable.getSeverity(existedScopesStatesAndNonExistNames.getExistedStates()); + return new SeverityState(ScopesAndSeveritiesTable.getSeverity(existedScopesStatesAndNonExistNames.getExistedStates()), true); } @Nullable @@ -314,6 +342,7 @@ public class ScopesAndSeveritiesTable extends JBTable { } } myScopeNames = ArrayUtil.toStringArray(scopesNames); + Arrays.sort(myScopeNames, myScopeComparator); } private int lastRowIndex() { @@ -326,13 +355,14 @@ public class ScopesAndSeveritiesTable extends JBTable { return; } if (columnIndex == SEVERITY_COLUMN) { - final HighlightDisplayLevel level = HighlightDisplayLevel.find(((HighlightSeverity)value).getName()); + final SeverityState severityState = (SeverityState)value; + final HighlightDisplayLevel level = HighlightDisplayLevel.find(severityState.getSeverity().getName()); if (level == null) { - LOG.error("no display level found for name " + ((HighlightSeverity)value).getName()); + LOG.error("no display level found for name " + severityState.getSeverity().getName()); return; } - final int idx = rowIndex == lastRowIndex() ? -1 : rowIndex; - myInspectionProfile.setErrorLevel(myKeys, level, idx, myProject); + final String scopeName = rowIndex == lastRowIndex() ? null : getScope(rowIndex).getName(); + myInspectionProfile.setErrorLevel(myKeys, level, scopeName, myProject); } else if (columnIndex == SCOPE_ENABLED_COLUMN) { final NamedScope scope = getScope(rowIndex); @@ -354,7 +384,7 @@ public class ScopesAndSeveritiesTable extends JBTable { } } } - myTableSettings.onChange(); + myTableSettings.onSettingsChanged(); } @Override @@ -368,9 +398,31 @@ public class ScopesAndSeveritiesTable extends JBTable { @Override public void addRow() { - AddScopeUtil.performAddScope(myTreeTable, myProject, myInspectionProfile, myNodes); - myTableSettings.onScopeAdded(); - refreshAggregatedScopes(); + final List<Descriptor> descriptors = ContainerUtil.map(myTableSettings.getNodes(), new Function<InspectionConfigTreeNode, Descriptor>() { + @Override + public Descriptor fun(InspectionConfigTreeNode inspectionConfigTreeNode) { + return inspectionConfigTreeNode.getDefaultDescriptor(); + } + }); + final ScopesChooser scopesChooser = new ScopesChooser(descriptors, myInspectionProfile, myProject, myScopeNames) { + @Override + protected void onScopeAdded() { + myTableSettings.onScopeAdded(); + refreshAggregatedScopes(); + } + + @Override + protected void onScopesOrderChanged() { + myTableSettings.onScopesOrderChanged(); + } + }; + DataContext dataContext = DataManager.getInstance().getDataContext(myTable); + final JComponent component = (JComponent)PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext); + final ListPopup popup = JBPopupFactory.getInstance() + .createActionGroupPopup(ScopesChooser.TITLE, scopesChooser.createPopupActionGroup(myTable), dataContext, + JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false); + final RelativePoint point = new RelativePoint(myTable, new Point(myTable.getWidth() - popup.getContent().getPreferredSize().width, 0)); + popup.show(point); } @Override diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/SeverityRenderer.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/SeverityRenderer.java index 2fe95e6d7224..e1b3d54b8fb7 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/SeverityRenderer.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/SeverityRenderer.java @@ -22,6 +22,8 @@ import com.intellij.openapi.ui.ComboBoxTableRenderer; import com.intellij.profile.codeInspection.SeverityProvider; import com.intellij.profile.codeInspection.ui.LevelChooserAction; import com.intellij.profile.codeInspection.ui.SingleInspectionProfilePanel; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -32,33 +34,40 @@ import java.util.SortedSet; /** * @author Dmitry Batkovich */ -public class SeverityRenderer extends ComboBoxTableRenderer<HighlightSeverity> { - public SeverityRenderer(final HighlightSeverity[] values) { +public class SeverityRenderer extends ComboBoxTableRenderer<SeverityState> { + public SeverityRenderer(final SeverityState[] values) { super(values); } public static SeverityRenderer create(final InspectionProfileImpl inspectionProfile) { final SortedSet<HighlightSeverity> severities = LevelChooserAction.getSeverities(((SeverityProvider)inspectionProfile.getProfileManager()).getOwnSeverityRegistrar()); - return new SeverityRenderer(severities.toArray(new HighlightSeverity[severities.size()])); + return new SeverityRenderer(ContainerUtil.map2Array(severities, new SeverityState[severities.size()], new Function<HighlightSeverity, SeverityState>() { + @Override + public SeverityState fun(HighlightSeverity severity) { + return new SeverityState(severity, true); + } + })); } + @Override + protected void customizeComponent(SeverityState value, JTable table, boolean isSelected) { + super.customizeComponent(value, table, isSelected); + setPaintArrow(value.isEnabledForEditing()); + } @Override - protected String getTextFor(@NotNull final HighlightSeverity value) { - return SingleInspectionProfilePanel.renderSeverity(value); + protected String getTextFor(@NotNull final SeverityState value) { + return SingleInspectionProfilePanel.renderSeverity(value.getSeverity()); } @Override - protected Icon getIconFor(@NotNull final HighlightSeverity value) { - return HighlightDisplayLevel.find(value).getIcon(); + protected Icon getIconFor(@NotNull final SeverityState value) { + return HighlightDisplayLevel.find(value.getSeverity()).getIcon(); } @Override public boolean isCellEditable(final EventObject event) { - if (event instanceof MouseEvent) { - return ((MouseEvent)event).getClickCount() >= 1; - } - return true; + return !(event instanceof MouseEvent) || ((MouseEvent)event).getClickCount() >= 1; } } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/SeverityState.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/SeverityState.java new file mode 100644 index 000000000000..2aefde070af7 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/SeverityState.java @@ -0,0 +1,40 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.profile.codeInspection.ui.table; + +import com.intellij.lang.annotation.HighlightSeverity; + +/** + * @author Dmitry Batkovich + */ +public class SeverityState { + + private final HighlightSeverity mySeverity; + private final boolean myEnabledForEditing; + + public SeverityState(HighlightSeverity severity, boolean enabledForEditing) { + mySeverity = severity; + myEnabledForEditing = enabledForEditing; + } + + public HighlightSeverity getSeverity() { + return mySeverity; + } + + public boolean isEnabledForEditing() { + return myEnabledForEditing; + } +} diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ThreeStateCheckBoxRenderer.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ThreeStateCheckBoxRenderer.java index 7d8cfdfc9db5..455e2a60cd19 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ThreeStateCheckBoxRenderer.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ThreeStateCheckBoxRenderer.java @@ -15,11 +15,9 @@ */ package com.intellij.profile.codeInspection.ui.table; -import com.intellij.ui.ClickListener; import com.intellij.util.SmartList; import com.intellij.util.ui.ThreeStateCheckBox; import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -28,7 +26,7 @@ import javax.swing.event.ChangeEvent; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import java.awt.*; -import java.awt.event.MouseEvent; +import java.awt.event.*; import java.util.EventObject; import java.util.List; @@ -39,10 +37,16 @@ public class ThreeStateCheckBoxRenderer extends ThreeStateCheckBox implements Ta private final List<CellEditorListener> myListeners = new SmartList<CellEditorListener>(); - public ThreeStateCheckBoxRenderer() { + public ThreeStateCheckBoxRenderer(final boolean isEditor) { setThirdStateEnabled(false); setHorizontalAlignment(CENTER); setVerticalAlignment(CENTER); + addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + stopCellEditing(); + } + }); } @Override @@ -69,16 +73,6 @@ public class ThreeStateCheckBoxRenderer extends ThreeStateCheckBox implements Ta } else { setSelected((Boolean) value); } - new ClickListener() { - @Override - public boolean onClick(@NotNull final MouseEvent event, final int clickCount) { - if (clickCount == 1) { - stopCellEditing(); - return true; - } - return false; - } - }.installOn(this); return this; } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java b/platform/lang-impl/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java deleted file mode 100644 index 2add3ed1b3c5..000000000000 --- a/platform/lang-impl/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.impl.search; - -import com.intellij.lexer.Lexer; -import com.intellij.lexer.LexerBase; -import com.intellij.openapi.editor.Document; -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.impl.EditorHighlighterCache; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.psi.impl.cache.impl.id.PlatformIdTableBuilding; -import com.intellij.psi.tree.IElementType; -import com.intellij.util.text.CharSequenceSubSequence; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** -* @author Sergey Evdokimov -*/ -public class LexerEditorHighlighterLexer extends LexerBase { - private HighlighterIterator iterator; - private CharSequence buffer; - private int start; - private int end; - private final EditorHighlighter myHighlighter; - private final boolean myAlreadyInitializedHighlighter; - - public LexerEditorHighlighterLexer(final EditorHighlighter highlighter, boolean alreadyInitializedHighlighter) { - myHighlighter = highlighter; - myAlreadyInitializedHighlighter = alreadyInitializedHighlighter; - } - - @Nullable - public static Lexer getLexerBasedOnLexerHighlighter(CharSequence text, VirtualFile virtualFile, Project project) { - EditorHighlighter highlighter = null; - - PsiFile psiFile = virtualFile != null ? PsiManager.getInstance(project).findFile(virtualFile) : null; - final Document document = psiFile != null ? PsiDocumentManager.getInstance(project).getDocument(psiFile) : null; - final EditorHighlighter cachedEditorHighlighter; - boolean alreadyInitializedHighlighter = false; - - if (document != null && - (cachedEditorHighlighter = EditorHighlighterCache.getEditorHighlighterForCachesBuilding(document)) != null && - PlatformIdTableBuilding.checkCanUseCachedEditorHighlighter(text, cachedEditorHighlighter)) { - highlighter = cachedEditorHighlighter; - alreadyInitializedHighlighter = true; - } - else if (virtualFile != null) { - highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, virtualFile); - } - - if (highlighter != null) { - return new LexerEditorHighlighterLexer(highlighter, alreadyInitializedHighlighter); - } - return null; - } - - @Override - public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int state) { - if (myAlreadyInitializedHighlighter) { - this.buffer = buffer; - start = startOffset; - end = endOffset; - } else { - myHighlighter.setText(new CharSequenceSubSequence(this.buffer = buffer, start = startOffset, end = endOffset)); - } - iterator = myHighlighter.createIterator(0); - } - - public void resetPosition(int offset) { - iterator = myHighlighter.createIterator(offset); - } - - @Override - public int getState() { - return 0; - } - - @Override - public IElementType getTokenType() { - if (iterator.atEnd()) return null; - return iterator.getTokenType(); - } - - @Override - public int getTokenStart() { - return iterator.getStart(); - } - - @Override - public int getTokenEnd() { - return iterator.getEnd(); - } - - @Override - public void advance() { - iterator.advance(); - } - - @NotNull - @Override - public CharSequence getBufferSequence() { - return buffer; - } - - @Override - public int getBufferEnd() { - return end; - } - - public HighlighterIterator getHighlighterIterator() { - return iterator; - } -} diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerRunnable.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerRunnable.java index 1b17faf21905..42dcb46c7ac0 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerRunnable.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerRunnable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 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. @@ -149,7 +149,7 @@ abstract class CodeStyleManagerRunnable<T> { SourceTreeToPsiMap.psiElementToTree(CodeStyleManagerImpl.findElementInTreeWithFormatterEnabled(file, offset)); if (elementAtOffset == null) { int significantRangeStart = CharArrayUtil.shiftBackward(file.getText(), offset - 1, "\r\t "); - return new TextRange(significantRangeStart, offset); + return new TextRange(Math.max(significantRangeStart, 0), offset); } final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java index 27089fa70e91..fdc434d8d11a 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.source.tree.injected; +import com.intellij.extapi.psi.PsiFileBase; import com.intellij.injected.editor.*; import com.intellij.lang.Language; import com.intellij.lang.LanguageUtil; @@ -48,8 +49,10 @@ import java.util.List; * @author cdr */ public class InjectedLanguageUtil { - static final Key<List<Trinity<IElementType, SmartPsiElementPointer<PsiLanguageInjectionHost>, TextRange>>> HIGHLIGHT_TOKENS = Key.create("HIGHLIGHT_TOKENS"); - public static Key<Boolean> FRANKENSTEIN_INJECTION = Key.create("FRANKENSTEIN_INJECTION"); // meaning: injected file text is probably incorrect + static final Key<List<Trinity<IElementType, SmartPsiElementPointer<PsiLanguageInjectionHost>, TextRange>>> HIGHLIGHT_TOKENS = + Key.create("HIGHLIGHT_TOKENS"); + public static Key<Boolean> FRANKENSTEIN_INJECTION = Key.create("FRANKENSTEIN_INJECTION"); + // meaning: injected file text is probably incorrect public static void forceInjectionOnElement(@NotNull PsiElement host) { enumerate(host, new PsiLanguageInjectionHost.InjectedPsiVisitor() { @@ -113,9 +116,9 @@ public class InjectedLanguageUtil { * @return true if enumerated successfully */ public static boolean enumerate(@NotNull PsiElement host, - @NotNull PsiFile containingFile, - boolean probeUp, - @NotNull PsiLanguageInjectionHost.InjectedPsiVisitor visitor) { + @NotNull PsiFile containingFile, + boolean probeUp, + @NotNull PsiLanguageInjectionHost.InjectedPsiVisitor visitor) { //do not inject into nonphysical files except during completion if (!containingFile.isPhysical() && containingFile.getOriginalFile() == containingFile) { final PsiElement context = InjectedLanguageManager.getInstance(containingFile.getProject()).getInjectionHost(containingFile); @@ -175,6 +178,32 @@ public class InjectedLanguageUtil { return null; } + /** + * Finds injected language in expression + * + * @param expression where to find + * @param classToFind class that represents language we look for + * @param <T> class that represents language we look for + * @return instance of class that represents language we look for or null of not found + */ + @Nullable + @SuppressWarnings("unchecked") // We check types dynamically (using isAssignableFrom) + public static <T extends PsiFileBase> T findInjectedFile(@NotNull final PsiElement expression, + @NotNull final Class<T> classToFind) { + final List<Pair<PsiElement, TextRange>> files = + InjectedLanguageManager.getInstance(expression.getProject()).getInjectedPsiFiles(expression); + if (files == null) { + return null; + } + for (final Pair<PsiElement, TextRange> fileInfo : files) { + final PsiElement injectedFile = fileInfo.first; + if (classToFind.isAssignableFrom(injectedFile.getClass())) { + return (T)injectedFile; + } + } + return null; + } + public static Editor getEditorForInjectedLanguageNoCommit(@Nullable Editor editor, @Nullable PsiFile file, final int offset) { if (editor == null || file == null || editor instanceof EditorWindow) return editor; PsiFile injectedFile = findInjectedPsiNoCommit(file, offset); @@ -200,7 +229,9 @@ public class InjectedLanguageUtil { } } } - if (!documentWindow.isValid()) return hostEditor; // since the moment we got hold of injectedFile and this moment call, document may have been dirtied + if (!documentWindow.isValid()) { + return hostEditor; // since the moment we got hold of injectedFile and this moment call, document may have been dirtied + } return EditorWindowImpl.create(documentWindow, (EditorImpl)hostEditor, injectedFile); } @@ -284,7 +315,8 @@ public class InjectedLanguageUtil { ParameterizedCachedValue<MultiHostRegistrarImpl, PsiElement> cachedValue = CachedValuesManager.getManager(project).createParameterizedCachedValue(INJECTED_PSI_PROVIDER, false); - CachedValueProvider.Result<MultiHostRegistrarImpl> result = CachedValueProvider.Result.create(registrar, PsiModificationTracker.MODIFICATION_COUNT, registrar); + CachedValueProvider.Result<MultiHostRegistrarImpl> result = + CachedValueProvider.Result.create(registrar, PsiModificationTracker.MODIFICATION_COUNT, registrar); ((PsiParameterizedCachedValue<MultiHostRegistrarImpl, PsiElement>)cachedValue).setValue(result); e.putUserData(INJECTED_PSI, cachedValue); @@ -307,7 +339,9 @@ public class InjectedLanguageUtil { // returns (injected psi, leaf element at the offset, language of the leaf element) // since findElementAt() is expensive, we trying to reuse its result @NotNull - private static Trinity<PsiElement,PsiElement,Language> tryOffset(@NotNull PsiFile hostFile, final int offset, @NotNull PsiDocumentManager documentManager) { + private static Trinity<PsiElement, PsiElement, Language> tryOffset(@NotNull PsiFile hostFile, + final int offset, + @NotNull PsiDocumentManager documentManager) { FileViewProvider provider = hostFile.getViewProvider(); Language leafLanguage = null; PsiElement leafElement = null; @@ -319,11 +353,11 @@ public class InjectedLanguageUtil { leafElement = element; } PsiElement injected = findInside(element, hostFile, offset, documentManager); - if (injected != null) return Trinity.create(injected,element, language); + if (injected != null) return Trinity.create(injected, element, language); } // maybe we are at the border between two psi elements, then try to find injection at the end of the left element if (offset != 0 && (element == null || element.getTextRange().getStartOffset() == offset)) { - PsiElement leftElement = provider.findElementAt(offset-1, language); + PsiElement leftElement = provider.findElementAt(offset - 1, language); if (leftElement != null && leftElement.getTextRange().getEndOffset() == offset) { PsiElement injected = findInside(leftElement, hostFile, offset, documentManager); if (injected != null) return Trinity.create(injected, element, language); @@ -334,7 +368,10 @@ public class InjectedLanguageUtil { return Trinity.create(null, leafElement, leafLanguage); } - private static PsiElement findInside(@NotNull PsiElement element, @NotNull PsiFile hostFile, final int hostOffset, @NotNull final PsiDocumentManager documentManager) { + private static PsiElement findInside(@NotNull PsiElement element, + @NotNull PsiFile hostFile, + final int hostOffset, + @NotNull final PsiDocumentManager documentManager) { final Ref<PsiElement> out = new Ref<PsiElement>(); enumerate(element, hostFile, true, new PsiLanguageInjectionHost.InjectedPsiVisitor() { @Override @@ -361,10 +398,12 @@ public class InjectedLanguageUtil { // modification of cachedInjectedDocuments must be under PsiLock only ConcurrentList<DocumentWindow> injected = hostPsiFile.getUserData(INJECTED_DOCS_KEY); if (injected == null) { - injected = ((UserDataHolderEx)hostPsiFile).putUserDataIfAbsent(INJECTED_DOCS_KEY, ContainerUtil.<DocumentWindow>createConcurrentList()); + injected = + ((UserDataHolderEx)hostPsiFile).putUserDataIfAbsent(INJECTED_DOCS_KEY, ContainerUtil.<DocumentWindow>createConcurrentList()); } return injected; } + public static void clearCachedInjectedFragmentsForFile(@NotNull PsiFile file) { file.putUserData(INJECTED_DOCS_KEY, null); } @@ -426,10 +465,12 @@ public class InjectedLanguageUtil { } return containingFile; } + @NotNull public static Editor getTopLevelEditor(@NotNull Editor editor) { return editor instanceof EditorWindow ? ((EditorWindow)editor).getDelegate() : editor; } + public static boolean isInInjectedLanguagePrefixSuffix(@NotNull final PsiElement element) { PsiFile injectedFile = element.getContainingFile(); if (injectedFile == null) return false; @@ -461,13 +502,13 @@ public class InjectedLanguageUtil { public static String getUnescapedText(PsiFile file, @Nullable final PsiElement startElement, @Nullable final PsiElement endElement) { final InjectedLanguageManager manager = InjectedLanguageManager.getInstance(file.getProject()); if (manager.getInjectionHost(file) == null) { - return file.getText().substring(startElement == null? 0 : startElement.getTextRange().getStartOffset(), - endElement == null? file.getTextLength() : endElement.getTextRange().getStartOffset()); + return file.getText().substring(startElement == null ? 0 : startElement.getTextRange().getStartOffset(), + endElement == null ? file.getTextLength() : endElement.getTextRange().getStartOffset()); } final StringBuilder sb = new StringBuilder(); file.accept(new PsiRecursiveElementWalkingVisitor() { - Boolean myState = startElement == null? Boolean.TRUE : null; + Boolean myState = startElement == null ? Boolean.TRUE : null; @Override public void visitElement(PsiElement element) { diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/KeyboardComboSwitcher.java b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/KeyboardComboSwitcher.java index 99ac9f7ec586..232ddd7bec3b 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/KeyboardComboSwitcher.java +++ b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/KeyboardComboSwitcher.java @@ -28,7 +28,7 @@ public class KeyboardComboSwitcher { if (toggleStrategy) { final int size = comboBox.getModel().getSize(); int next = comboBox.getSelectedIndex() + 1; - if (next < 0 || next >= size) { + if (size > 0 && (next < 0 || next >= size)) { if (!UISettings.getInstance().CYCLE_SCROLLING) { return; } diff --git a/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java b/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java index 1301079a1967..6e37711a9493 100644 --- a/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java +++ b/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java @@ -151,6 +151,7 @@ public class DetailViewImpl extends JPanel implements DetailView, UserDataHolder getEditor().getSettings().setRefrainFromScrolling(false); getEditor().getSettings().setLineNumbersShown(true); getEditor().getSettings().setFoldingOutlineShown(false); + ((EditorEx)getEditor()).getFoldingModel().setFoldingEnabled(false); add(getEditor().getComponent(), BorderLayout.CENTER); } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/ContentHashesSupport.java b/platform/lang-impl/src/com/intellij/util/indexing/ContentHashesSupport.java index 34f80dae5cc9..451b0e7c1954 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ContentHashesSupport.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ContentHashesSupport.java @@ -84,7 +84,7 @@ class ContentHashesSupport { messageDigest.update((byte)0); messageDigest.update(String.valueOf(bytes.length).getBytes(defaultCharset)); messageDigest.update((byte)0); - messageDigest.update((charset != null ? charset.displayName():"null_charset").getBytes(defaultCharset)); + messageDigest.update((charset != null ? charset.name():"null_charset").getBytes(defaultCharset)); messageDigest.update((byte)0); messageDigest.update(bytes, 0, bytes.length); diff --git a/platform/lang-impl/src/com/intellij/util/indexing/DebugAssertions.java b/platform/lang-impl/src/com/intellij/util/indexing/DebugAssertions.java index 6c866ad2283f..09aeea33b71e 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/DebugAssertions.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/DebugAssertions.java @@ -31,7 +31,7 @@ public class DebugAssertions { public static final boolean EXTRA_SANITY_CHECKS = SystemProperties.getBooleanProperty( "intellij.idea.indices.debug.extra.sanity", - true + DEBUG ); public static void assertTrue(boolean value) { diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index 5242ee3b70cf..85077380ea6e 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -1964,33 +1964,38 @@ public class FileBasedIndexImpl extends FileBasedIndex { // For 'normal indices' schedule the file for update and reset stamps for all affected indices (there // can be client that used indices between before and after events, in such case indices are up to date due to force update // with old content) - if (!fileIsDirectory && !isTooLarge(file)) { - FileTypeManagerImpl.cacheFileType(file, file.getFileType()); - try { - final List<ID<?, ?>> candidates = getAffectedIndexCandidates(file); - //noinspection ForLoopReplaceableByForEach - boolean scheduleForUpdate = false; - boolean resetStamp = false; - - //noinspection ForLoopReplaceableByForEach - for (int i = 0, size = candidates.size(); i < size; ++i) { - final ID<?, ?> indexId = candidates.get(i); - if (needsFileContentLoading(indexId) && getInputFilter(indexId).acceptInput(file)) { - if (IndexingStamp.isFileIndexedStateCurrent(file, indexId)) { - IndexingStamp.setFileIndexedStateOutdated(file, indexId); - resetStamp = true; + if (!fileIsDirectory) { + if (isTooLarge(file)) { + // large file might be scheduled for update in before event when its size was not large + myChangedFilesCollector.myFilesToUpdate.remove(file); + } else { + FileTypeManagerImpl.cacheFileType(file, file.getFileType()); + try { + final List<ID<?, ?>> candidates = getAffectedIndexCandidates(file); + //noinspection ForLoopReplaceableByForEach + boolean scheduleForUpdate = false; + boolean resetStamp = false; + + //noinspection ForLoopReplaceableByForEach + for (int i = 0, size = candidates.size(); i < size; ++i) { + final ID<?, ?> indexId = candidates.get(i); + if (needsFileContentLoading(indexId) && getInputFilter(indexId).acceptInput(file)) { + if (IndexingStamp.isFileIndexedStateCurrent(file, indexId)) { + IndexingStamp.setFileIndexedStateOutdated(file, indexId); + resetStamp = true; + } + scheduleForUpdate = true; } - scheduleForUpdate = true; } - } - if (scheduleForUpdate) { - if (resetStamp) IndexingStamp.flushCache(file); - scheduleForUpdate(file); + if (scheduleForUpdate) { + if (resetStamp) IndexingStamp.flushCache(file); + scheduleForUpdate(file); + } + } + finally { + FileTypeManagerImpl.cacheFileType(file, null); } - } - finally { - FileTypeManagerImpl.cacheFileType(file, null); } } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/IndexingStamp.java b/platform/lang-impl/src/com/intellij/util/indexing/IndexingStamp.java index 6bdd55b1d343..5ef1379eaaf4 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/IndexingStamp.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/IndexingStamp.java @@ -59,7 +59,7 @@ public class IndexingStamp { private static final long UNINDEXED_STAMP = -1L; // we don't store trivial "absent" state private static final long INDEX_DATA_OUTDATED_STAMP = -2L; - private static final int VERSION = 12; + private static final int VERSION = 13; private static final ConcurrentHashMap<ID<?, ?>, Long> ourIndexIdToCreationStamp = new ConcurrentHashMap<ID<?, ?>, Long>(); private static volatile long ourLastStamp; // ensure any file index stamp increases diff --git a/platform/lang-impl/src/com/intellij/util/indexing/MapReduceIndex.java b/platform/lang-impl/src/com/intellij/util/indexing/MapReduceIndex.java index ebb7911e82be..6427765295c5 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/MapReduceIndex.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/MapReduceIndex.java @@ -354,7 +354,7 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val FileContent fileContent = (FileContent)content; hashId = getHashOfContent(fileContent); if (doReadSavedPersistentData) { - if (!myContents.isBusyReading()) { // avoid blocking read, we can calculate index value + if (!myContents.isBusyReading() || DebugAssertions.EXTRA_SANITY_CHECKS) { // avoid blocking read, we can calculate index value ByteSequence bytes = myContents.get(hashId); if (bytes != null) { data = deserializeSavedPersistentData(bytes); @@ -367,7 +367,7 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val "Unexpected difference in indexing of %s by index %s, file type %s, charset %s\ndiff %s\nprevious indexed info %s", fileContent.getFile(), myIndexId, - fileContent.getFileType(), + fileContent.getFileType().getName(), ((FileContentImpl)fileContent).getCharset(), buildDiff(data, contentData), myIndexingTrace.get(hashId) @@ -396,7 +396,7 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val FileContent fileContent = (FileContent)content; try { - myIndexingTrace.put(hashId, ((FileContentImpl)fileContent).getCharset() + "," + fileContent.getFileType()+"," + fileContent.getFile().getPath() + "," + + myIndexingTrace.put(hashId, ((FileContentImpl)fileContent).getCharset() + "," + fileContent.getFileType().getName()+"," + fileContent.getFile().getPath() + "," + ExceptionUtil.getThrowableText(new Throwable())); } catch (IOException ex) { LOG.error(ex); diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java b/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java index 18961ca843c3..fcbaa21f330b 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java @@ -1,3 +1,18 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.webcore.packaging; import com.intellij.icons.AllIcons; @@ -93,6 +108,7 @@ public class ManagePackagesDialog extends DialogWrapper { public void run() { try { myController.reloadAllPackages(); + initModel(); myPackages.setPaintBusy(false); } catch (final IOException e) { @@ -310,6 +326,7 @@ public class ManagePackagesDialog extends DialogWrapper { @Override public void run() { myPackages.setModel(myPackagesModel); + ((MyPackageFilter)myFilter).filter(); doSelectPackage(mySelectedPackageName); setDownloadStatus(false); } @@ -473,6 +490,7 @@ public class ManagePackagesDialog extends DialogWrapper { final Object pyPackage = myPackages.getSelectedValue(); if (pyPackage instanceof RepoPackage) { final String packageName = ((RepoPackage)pyPackage).getName(); + mySelectedPackageName = packageName; myVersionComboBox.removeAllItems(); if (myVersionCheckBox.isEnabled()) { myController.fetchPackageVersions(packageName, new CatchingConsumer<List<String>, Exception>() { diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java index cb281cb3d7af..1513bb451aa2 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java @@ -8,6 +8,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.HyperlinkAdapter; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBLabel; +import com.intellij.util.ui.SwingHelper; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -22,18 +23,17 @@ import java.util.Map; * @author yole */ public class PackagesNotificationPanel { - private final JEditorPane myEditorPane = new MyNotificationPane(); private final Project myProject; + private final JEditorPane myHtmlViewer; private final Map<String, Runnable> myLinkHandlers = new HashMap<String, Runnable>(); private String myErrorTitle; private String myErrorDescription; - public PackagesNotificationPanel(Project project) { + public PackagesNotificationPanel(@NotNull Project project) { myProject = project; - myEditorPane.setBackground(UIManager.getColor("ArrowButton.background")); - myEditorPane.setContentType("text/html"); - myEditorPane.setEditable(false); - myEditorPane.addHyperlinkListener(new HyperlinkAdapter() { + myHtmlViewer = SwingHelper.createHtmlViewer(true, null, null, null); + myHtmlViewer.setVisible(false); + myHtmlViewer.addHyperlinkListener(new HyperlinkAdapter() { @Override protected void hyperlinkActivated(HyperlinkEvent e) { final Runnable handler = myLinkHandlers.get(e.getDescription()); @@ -76,11 +76,18 @@ public class PackagesNotificationPanel { public void showResult(String packageName, @Nullable String errorDescription) { if (StringUtil.isEmpty(errorDescription)) { - showSuccess("Package successfully installed."); + String message = "Package installed successfully"; + if (packageName != null) { + message = "Package '" + packageName + "' installed successfully"; + } + showSuccess(message); } else { - String title = "Install packages failed"; - final String firstLine = title + ": Error occurred when installing package " + packageName + ". "; + String title = "Failed to install packages"; + if (packageName != null) { + title = "Failed to install package '" + packageName + "'"; + } + String firstLine = "Error occurred when installing package '" + packageName + "'. "; showError(firstLine + "<a href=\"xxx\">Details...</a>", title, firstLine + errorDescription); @@ -96,19 +103,18 @@ public class PackagesNotificationPanel { } public JComponent getComponent() { - return myEditorPane; + return myHtmlViewer; } public void showSuccess(String text) { showContent(text, MessageType.INFO.getPopupBackground()); } - private void showContent(String text, final Color background) { - myEditorPane.removeAll(); + private void showContent(@NotNull String text, @NotNull Color background) { String htmlText = text.startsWith("<html>") ? text : UIUtil.toHtml(text); - myEditorPane.setText(htmlText); - myEditorPane.setBackground(background); - myEditorPane.setVisible(true); + myHtmlViewer.setText(htmlText); + myHtmlViewer.setBackground(background); + setVisibleEditorPane(true); myErrorTitle = null; myErrorDescription = null; } @@ -124,22 +130,20 @@ public class PackagesNotificationPanel { } public void hide() { - myEditorPane.setVisible(false); + setVisibleEditorPane(false); } - public boolean hasLinkHandler(String key) { - return myLinkHandlers.containsKey(key); + private void setVisibleEditorPane(boolean visible) { + boolean oldVisible = myHtmlViewer.isVisible(); + myHtmlViewer.setVisible(visible); + if (oldVisible != visible) { + myHtmlViewer.revalidate(); + myHtmlViewer.repaint(); + } } - private static class MyNotificationPane extends JEditorPane { - @Override - public Dimension getPreferredSize() { - // This trick makes text component to carry text over to the next line - // iff the text line width exceeds parent's width - Dimension dimension = super.getPreferredSize(); - dimension.width = 0; - return dimension; - } + public boolean hasLinkHandler(String key) { + return myLinkHandlers.containsKey(key); } public void removeLinkHandler(String key) { |