diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionListStep.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionListStep.java index 6bc0bfef71f7..85bf449092e2 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionListStep.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionListStep.java @@ -53,7 +53,7 @@ import java.util.*; /** * @author cdr */ -class IntentionListStep implements ListPopupStep, SpeedSearchFilter { +public class IntentionListStep implements ListPopupStep, SpeedSearchFilter { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.IntentionListStep"); private final Set myCachedIntentions = @@ -65,6 +65,7 @@ class IntentionListStep implements ListPopupStep private final IntentionManagerSettings mySettings; @Nullable private final IntentionHintComponent myIntentionHintComponent; + @Nullable private final Editor myEditor; private final PsiFile myFile; private final Project myProject; @@ -81,9 +82,9 @@ class IntentionListStep implements ListPopupStep }; private Runnable myFinalRunnable; - IntentionListStep(@Nullable IntentionHintComponent intentionHintComponent, + public IntentionListStep(@Nullable IntentionHintComponent intentionHintComponent, @NotNull ShowIntentionsPass.IntentionsInfo intentions, - @NotNull Editor editor, + @Nullable Editor editor, @NotNull PsiFile file, @NotNull Project project) { this(intentionHintComponent, editor, file, project); @@ -91,7 +92,7 @@ class IntentionListStep implements ListPopupStep } IntentionListStep(@Nullable IntentionHintComponent intentionHintComponent, - @NotNull Editor editor, + @Nullable Editor editor, @NotNull PsiFile file, @NotNull Project project) { myIntentionHintComponent = intentionHintComponent; @@ -113,7 +114,7 @@ class IntentionListStep implements ListPopupStep private boolean wrapActionsTo(@NotNull List newDescriptors, @NotNull Set cachedActions, boolean callUpdate) { - final int caretOffset = myEditor.getCaretModel().getOffset(); + final int caretOffset = myEditor == null ? 0 : myEditor.getCaretModel().getOffset(); final int fileOffset = caretOffset > 0 && caretOffset == myFile.getTextLength() ? caretOffset - 1 : caretOffset; PsiElement element; final PsiElement hostElement; @@ -121,7 +122,7 @@ class IntentionListStep implements ListPopupStep hostElement = element = myFile; } - else if (PsiDocumentManager.getInstance(myProject).isUncommited(myEditor.getDocument())) { + else if (myEditor != null && PsiDocumentManager.getInstance(myProject).isUncommited(myEditor.getDocument())) { //??? FileViewProvider viewProvider = myFile.getViewProvider(); hostElement = element = viewProvider.findElementAt(fileOffset, viewProvider.getBaseLanguage()); @@ -138,14 +139,14 @@ class IntentionListStep implements ListPopupStep } else { injectedFile = element.getContainingFile(); - injectedEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(myEditor, injectedFile); + injectedEditor = myEditor == null ? null : InjectedLanguageUtil.getInjectedEditorForInjectedFile(myEditor, injectedFile); } boolean changed = false; for (Iterator iterator = cachedActions.iterator(); iterator.hasNext();) { IntentionActionWithTextCaching cachedAction = iterator.next(); IntentionAction action = cachedAction.getAction(); - if (!ShowIntentionActionsHandler.availableFor(myFile, myEditor, action) + if (myEditor != null && !ShowIntentionActionsHandler.availableFor(myFile, myEditor, action) && (hostElement == element || element != null && !ShowIntentionActionsHandler.availableFor(injectedFile, injectedEditor, action))) { iterator.remove(); changed = true; @@ -155,12 +156,12 @@ class IntentionListStep implements ListPopupStep Set wrappedNew = new THashSet(newDescriptors.size(), ACTION_TEXT_AND_CLASS_EQUALS); for (HighlightInfo.IntentionActionDescriptor descriptor : newDescriptors) { final IntentionAction action = descriptor.getAction(); - if (element != null && element != hostElement && (!callUpdate || ShowIntentionActionsHandler.availableFor(injectedFile, injectedEditor, action))) { + if (element != null && element != hostElement && (!callUpdate || myEditor == null || ShowIntentionActionsHandler.availableFor(injectedFile, injectedEditor, action))) { IntentionActionWithTextCaching cachedAction = wrapAction(descriptor, element, injectedFile, injectedEditor); wrappedNew.add(cachedAction); changed |= cachedActions.add(cachedAction); } - else if (hostElement != null && (!callUpdate || ShowIntentionActionsHandler.availableFor(myFile, myEditor, action))) { + else if (hostElement != null && (!callUpdate || myEditor == null || ShowIntentionActionsHandler.availableFor(myFile, myEditor, action))) { IntentionActionWithTextCaching cachedAction = wrapAction(descriptor, hostElement, myFile, myEditor); wrappedNew.add(cachedAction); changed |= cachedActions.add(cachedAction); @@ -181,7 +182,7 @@ class IntentionListStep implements ListPopupStep IntentionActionWithTextCaching wrapAction(@NotNull HighlightInfo.IntentionActionDescriptor descriptor, @NotNull PsiElement element, @NotNull PsiFile containingFile, - @NotNull Editor containingEditor) { + @Nullable Editor containingEditor) { IntentionActionWithTextCaching cachedAction = new IntentionActionWithTextCaching(descriptor); final List options = descriptor.getOptions(element, containingEditor); if (options == null) return cachedAction; @@ -252,12 +253,15 @@ class IntentionListStep implements ListPopupStep } PsiDocumentManager.getInstance(myProject).commitAllDocuments(); - final PsiFile file = PsiUtilBase.getPsiFileInEditor(myEditor, myProject); - if (file == null) { - return; + PsiFile file = null; + if (myEditor != null) { + file = PsiUtilBase.getPsiFileInEditor(myEditor, myProject); + if (file == null) { + return; + } } - ShowIntentionActionsHandler.chooseActionAndInvoke(file, myEditor, cachedAction.getAction(), cachedAction.getText()); + ShowIntentionActionsHandler.chooseActionAndInvoke(file, myEditor, cachedAction.getAction(), cachedAction.getText(), myProject); } }); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java index dba010c97e83..5145359b2efe 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java @@ -166,31 +166,39 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler { @NotNull final Editor hostEditor, @NotNull final IntentionAction action, @NotNull String text) { - if (!hostFile.isValid()) return false; final Project project = hostFile.getProject(); + return chooseActionAndInvoke(hostFile, hostEditor, action, text, project); + } + + public static boolean chooseActionAndInvoke(@Nullable PsiFile hostFile, + @Nullable final Editor hostEditor, + @NotNull final IntentionAction action, + @NotNull String text, + @NotNull final Project project) { FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.quickFix"); ((FeatureUsageTrackerImpl)FeatureUsageTracker.getInstance()).getFixesStats().registerInvocation(); - Pair pair = chooseBetweenHostAndInjected(hostFile, hostEditor, new PairProcessor() { + final Pair pair = hostEditor != null && hostFile != null + ? chooseBetweenHostAndInjected(hostFile, hostEditor, new PairProcessor() { @Override public boolean process(PsiFile psiFile, Editor editor) { return availableFor(psiFile, editor, action); } - }); + }) : Pair.create(null, null); if (pair == null) return false; - final Editor editorToApply = pair.second; - final PsiFile fileToApply = pair.first; Runnable runnable = new Runnable() { @Override public void run() { try { - action.invoke(project, editorToApply, fileToApply); + action.invoke(project, pair.second, pair.first); } catch (IncorrectOperationException e) { LOG.error(e); } - DaemonCodeAnalyzer.getInstance(project).updateVisibleHighlighters(hostEditor); + if (hostEditor != null) { + DaemonCodeAnalyzer.getInstance(project).updateVisibleHighlighters(hostEditor); + } } }; diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/DisableInspectionToolAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/DisableInspectionToolAction.java index 03ccca781ed5..a3441c91b59c 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/DisableInspectionToolAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/DisableInspectionToolAction.java @@ -28,6 +28,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Iconable; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.PsiFile; +import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -67,18 +68,27 @@ public class DisableInspectionToolAction extends IntentionAndQuickFixAction impl } @Override - public void applyFix(@NotNull Project project, PsiFile file, @Nullable Editor editor) { + public void applyFix(@NotNull Project project, final PsiFile file, @Nullable Editor editor) { + modifyAndCommitProjectProfile(new Consumer() { + @Override + public void consume(ModifiableModel modifiableModel) { + modifiableModel.disableTool(myToolId, file); + } + }, project); + DaemonCodeAnalyzer.getInstance(project).restart(); + } + + public static void modifyAndCommitProjectProfile(Consumer action, Project project) { InspectionProjectProfileManager profileManager = InspectionProjectProfileManager.getInstance(project); InspectionProfile inspectionProfile = profileManager.getInspectionProfile(); ModifiableModel model = inspectionProfile.getModifiableModel(); - model.disableTool(myToolId, file); + action.consume(model); try { model.commit(); } catch (IOException e) { Messages.showErrorDialog(project, e.getMessage(), CommonBundle.getErrorTitle()); } - DaemonCodeAnalyzer.getInstance(project).restart(); } @Override diff --git a/plugins/properties/properties-psi-api/resources/messages/PropertiesBundle.properties b/plugins/properties/properties-psi-api/resources/messages/PropertiesBundle.properties index 0a26d3bc5d5f..45b01000b797 100644 --- a/plugins/properties/properties-psi-api/resources/messages/PropertiesBundle.properties +++ b/plugins/properties/properties-psi-api/resources/messages/PropertiesBundle.properties @@ -78,4 +78,7 @@ create.resource.bundle.dialog.add.locales.validator.title=Add Locales add.property.files.to.resource.bundle.dialog.action.title=Add Property Files to Resource Bundle -resource.bundle.editor.settings.action.title=Resource bundle editor settings \ No newline at end of file +resource.bundle.editor.settings.action.title=Resource bundle editor settings +incomplete.property.inspection.display.name=Property is incomplete +incomplete.property.quick.fix.name=Ignore untranslated locales +incomplete.property.inspection.description=Property ''{0}'' is incomplete \ No newline at end of file diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/IgnoredPropertiesFilesSuffixesManager.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/IgnoredPropertiesFilesSuffixesManager.java deleted file mode 100644 index 8bb464b22a2b..000000000000 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/IgnoredPropertiesFilesSuffixesManager.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2000-2015 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.lang.properties.editor; - -import com.intellij.lang.properties.PropertiesUtil; -import com.intellij.lang.properties.ResourceBundle; -import com.intellij.lang.properties.psi.PropertiesFile; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.components.*; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Disposer; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.HashSet; -import com.intellij.util.xmlb.annotations.AbstractCollection; -import com.intellij.util.xmlb.annotations.Property; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * @author Dmitry Batkovich - */ -@State( - name = "IgnoredPropertiesFilesSuffixesManager", - storages = { - @Storage(file = StoragePathMacros.PROJECT_FILE), - @Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/resourceBundles.xml", scheme = StorageScheme.DIRECTORY_BASED) - }) -public class IgnoredPropertiesFilesSuffixesManager implements PersistentStateComponent, Disposable { - private IgnoredPropertiesFilesSuffixesState myState = new IgnoredPropertiesFilesSuffixesState(); - private final List myListeners = new ArrayList(); - - public IgnoredPropertiesFilesSuffixesManager(final Project project) { - Disposer.register(project, this); - } - - public static IgnoredPropertiesFilesSuffixesManager getInstance(final Project project) { - return ServiceManager.getService(project, IgnoredPropertiesFilesSuffixesManager.class); - } - - public void addListener(final SuffixesListener listener) { - myListeners.add(listener); - } - - public void removeListener(final SuffixesListener listener) { - myListeners.remove(listener); - } - - public boolean isPropertyComplete(final ResourceBundle resourceBundle, final String key) { - List propertiesFiles = resourceBundle.getPropertiesFiles(); - for (PropertiesFile propertiesFile : propertiesFiles) { - if (propertiesFile.findPropertyByKey(key) == null && !myState.getIgnoredSuffixes().contains(PropertiesUtil.getSuffix(propertiesFile))) { - return false; - } - } - return true; - } - - public Set getIgnoredSuffixes() { - return myState.getIgnoredSuffixes(); - } - - public List getPropertiesFilesWithoutTranslation(final ResourceBundle resourceBundle, final Set keys) { - final PropertiesFile defaultPropertiesFile = resourceBundle.getDefaultPropertiesFile(); - return ContainerUtil.filter(resourceBundle.getPropertiesFiles(), new Condition() { - @Override - public boolean value(PropertiesFile propertiesFile) { - if (defaultPropertiesFile.equals(propertiesFile)) { - return false; - } - for (String key : keys) { - if (propertiesFile.findPropertyByKey(key) == null && !myState.getIgnoredSuffixes().contains(PropertiesUtil.getSuffix(propertiesFile))) { - return true; - } - } - return false; - } - }); - } - - public void addSuffixes(Collection suffixes) { - final Set oldSuffixes = new HashSet(myState.getIgnoredSuffixes()); - myState.addSuffixes(suffixes); - final Set newSuffixes = myState.getIgnoredSuffixes(); - if (!oldSuffixes.equals(newSuffixes)) { - for (SuffixesListener listener : myListeners) { - listener.suffixesChanged(); - } - } - } - - public void setSuffixes(Collection suffixes) { - final Set oldSuffixes = myState.getIgnoredSuffixes(); - myState.setSuffixes(suffixes); - if (!oldSuffixes.equals(suffixes)) { - for (SuffixesListener listener : myListeners) { - listener.suffixesChanged(); - } - } - } - - @Nullable - @Override - public IgnoredPropertiesFilesSuffixesState getState() { - return myState.isEmpty() ? null : myState; - } - - @Override - public void loadState(IgnoredPropertiesFilesSuffixesState state) { - myState = state; - } - - @Override - public void dispose() { - if (!myListeners.isEmpty()) { - myListeners.clear(); - } - } - - public static class IgnoredPropertiesFilesSuffixesState { - @Property(surroundWithTag = false) - @AbstractCollection(elementTag = "ignored-suffix", surroundWithTag = false) - public Set myIgnoredSuffixes = new HashSet(); - - public void addSuffixes(Collection suffixes) { - myIgnoredSuffixes.addAll(suffixes); - } - - public void setSuffixes(Collection suffixes) { - myIgnoredSuffixes = new HashSet(suffixes); - } - - public Set getIgnoredSuffixes() { - return myIgnoredSuffixes; - } - - public boolean isEmpty() { - return myIgnoredSuffixes.isEmpty(); - } - } - - public interface SuffixesListener { - void suffixesChanged(); - } -} \ No newline at end of file diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java index edc61645c6f7..4b5404e6799f 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java @@ -22,13 +22,15 @@ package com.intellij.lang.properties.editor; import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.lang.properties.*; import com.intellij.lang.properties.ResourceBundle; -import com.intellij.navigation.ColoredItemPresentation; +import com.intellij.lang.properties.editor.inspections.ResourceBundleEditorInspectionPass; +import com.intellij.lang.properties.editor.inspections.ResourceBundleEditorProblemDescriptor; +import com.intellij.lang.properties.editor.inspections.ResourceBundleEditorRenderer; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.psi.PsiFile; -import com.intellij.ui.JBColor; import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -37,32 +39,23 @@ import javax.swing.*; import java.awt.*; public class ResourceBundlePropertyStructureViewElement implements StructureViewTreeElement, ResourceBundleEditorViewElement { - private final ResourceBundle myResourceBundle; - @NotNull private final PropertiesAnchorizer.PropertyAnchor myAnchor; - private String myPresentableName; - - private static final TextAttributesKey INCOMPLETE_PROPERTY_KEY; - private static final TextAttributesKey INCOMPLETE_GROUP_KEY; private static final TextAttributesKey GROUP_KEY; public static final String PROPERTY_GROUP_KEY_TEXT = ""; - static { - TextAttributes incompleteKeyTextAttributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(PropertiesHighlighter.PROPERTY_KEY).clone(); - incompleteKeyTextAttributes.setForegroundColor(JBColor.RED); - INCOMPLETE_PROPERTY_KEY = TextAttributesKey.createTextAttributesKey("INCOMPLETE_PROPERTY_KEY", incompleteKeyTextAttributes); + private final @NotNull PropertiesAnchorizer.PropertyAnchor myAnchor; + private String myPresentableName; + + static { TextAttributes groupKeyTextAttributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(PropertiesHighlighter.PROPERTY_KEY).clone(); groupKeyTextAttributes.setFontType(Font.ITALIC); GROUP_KEY = TextAttributesKey.createTextAttributesKey("GROUP_KEY", groupKeyTextAttributes); - - final TextAttributes incompleteGroupKeyTextAttribute = groupKeyTextAttributes.clone(); - incompleteGroupKeyTextAttribute.setForegroundColor(JBColor.RED); - INCOMPLETE_GROUP_KEY = TextAttributesKey.createTextAttributesKey("INCOMPLETE_GROUP_KEY", incompleteGroupKeyTextAttribute); } + private ResourceBundleEditorInspectionPass.InspectionPassInfo myInspectionPassInfo; + public ResourceBundlePropertyStructureViewElement(final ResourceBundle resourceBundle, final @NotNull PropertiesAnchorizer.PropertyAnchor anchor) { - myResourceBundle = resourceBundle; myAnchor = anchor; } @@ -97,10 +90,16 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView return EMPTY_ARRAY; } + @NotNull + public ResourceBundleEditorProblemDescriptor[] getProblemDescriptors() { + return myInspectionPassInfo.getDescriptors(); + } + @Override @NotNull public ItemPresentation getPresentation() { - return new ColoredItemPresentation() { + return new ResourceBundleEditorRenderer.TextAttributesPresentation() { + @Override public String getPresentableText() { return myPresentableName == null ? getProperty().getName() : myPresentableName.isEmpty() ? PROPERTY_GROUP_KEY_TEXT : myPresentableName; @@ -117,14 +116,15 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView } @Override - public TextAttributesKey getTextAttributesKey() { - final IgnoredPropertiesFilesSuffixesManager - ignoredPropertiesFilesSuffixesManager = IgnoredPropertiesFilesSuffixesManager.getInstance(myResourceBundle.getProject()); - final boolean isPropertyComplete = ignoredPropertiesFilesSuffixesManager.isPropertyComplete(myResourceBundle, getProperty().getName()); - if (myPresentableName != null && myPresentableName.isEmpty()) { - return isPropertyComplete ? GROUP_KEY : INCOMPLETE_GROUP_KEY; + public TextAttributes getTextAttributes(EditorColorsScheme colorsScheme) { + myInspectionPassInfo = ResourceBundleEditorInspectionPass.inspect(getProperty().getKey(), getProperty().getPropertiesFile().getResourceBundle()); + final TextAttributesKey baseAttrKey = (myPresentableName != null && myPresentableName.isEmpty()) ? GROUP_KEY : PropertiesHighlighter.PROPERTY_KEY; + final TextAttributes baseAttrs = colorsScheme.getAttributes(baseAttrKey); + TextAttributes highlightingAttributes = myInspectionPassInfo.getTextAttributes(colorsScheme); + if (highlightingAttributes != null) { + return TextAttributes.merge(baseAttrs, highlightingAttributes); } - return isPropertyComplete ? PropertiesHighlighter.PROPERTY_KEY : INCOMPLETE_PROPERTY_KEY; + return baseAttrs; } }; } diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorInspection.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorInspection.java new file mode 100644 index 000000000000..7648ffcba381 --- /dev/null +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorInspection.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2015 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.lang.properties.editor.inspections; + +import com.intellij.codeInspection.*; +import com.intellij.lang.properties.IProperty; +import com.intellij.lang.properties.ResourceBundle; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Dmitry Batkovich + */ +public abstract class ResourceBundleEditorInspection extends LocalInspectionTool { + + @Nullable + public abstract ResourceBundleEditorProblemDescriptor[] checkPropertyGroup(@NotNull List properties, + @NotNull ResourceBundle resourceBundle); + + @NotNull + @Override + public final PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return PsiElementVisitor.EMPTY_VISITOR; + } + + @NotNull + @Override + public final PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, + boolean isOnTheFly, + @NotNull LocalInspectionToolSession session) { + return PsiElementVisitor.EMPTY_VISITOR; + } + + @Nullable + @Override + public final ProblemDescriptor[] checkFile(@NotNull PsiFile file, + @NotNull InspectionManager manager, + boolean isOnTheFly) { + return null; + } +} diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorInspectionPass.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorInspectionPass.java new file mode 100644 index 000000000000..d3590824363e --- /dev/null +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorInspectionPass.java @@ -0,0 +1,131 @@ +/* + * Copyright 2000-2015 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.lang.properties.editor.inspections; + +import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInsight.daemon.impl.HighlightInfoType; +import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; +import com.intellij.codeInspection.ProblemDescriptorUtil; +import com.intellij.codeInspection.QuickFix; +import com.intellij.codeInspection.ex.InspectionProfileWrapper; +import com.intellij.codeInspection.ex.InspectionToolWrapper; +import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.lang.properties.IProperty; +import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.profile.codeInspection.InspectionProjectProfileManagerImpl; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.Function; +import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleEditorInspectionPass { + @Nullable + public static InspectionPassInfo inspect(@NotNull final String key, ResourceBundle resourceBundle) { + final List properties = + ContainerUtil.mapNotNull(resourceBundle.getPropertiesFiles(), new Function() { + @Override + public IProperty fun(PropertiesFile propertiesFile) { + return propertiesFile.findPropertyByKey(key); + } + }); + + if (properties.isEmpty()) { + return null; + } + final IProperty property = properties.get(0); + final PsiElement representativeElement = property.getPsiElement(); + final PsiFile representativeFile = representativeElement.getContainingFile(); + + final Project project = representativeElement.getProject(); + + InspectionProfileWrapper profileToUse = InspectionProjectProfileManagerImpl.getInstanceImpl(project).getProfileWrapper(); + final PsiFile containingFile = representativeFile.getContainingFile(); + final InspectionToolWrapper[] propertiesTools = profileToUse.getInspectionTools(containingFile); + + List allDescriptors = new SmartList(); + SortedSet highlightTypes = new TreeSet(new Comparator() { + @Override + public int compare(HighlightInfoType o1, HighlightInfoType o2) { + final HighlightSeverity s1 = o1.getSeverity(null); + final HighlightSeverity s2 = o2.getSeverity(null); + return Comparing.compare(s1, s2); + } + }); + + for (InspectionToolWrapper tool : propertiesTools) { + final HighlightDisplayKey toolKey; + if (tool.getTool() instanceof ResourceBundleEditorInspection && + profileToUse.isToolEnabled(toolKey = HighlightDisplayKey.find(tool.getShortName()), containingFile)) { + final ResourceBundleEditorInspection inspection = (ResourceBundleEditorInspection)tool.getTool(); + final ResourceBundleEditorProblemDescriptor[] descriptors = inspection.checkPropertyGroup(properties, resourceBundle); + if (descriptors != null) { + for (ResourceBundleEditorProblemDescriptor descriptor : descriptors) { + final QuickFix[] currentFixes = descriptor.getFixes(); + if (currentFixes != null && currentFixes.length != 0) { + Collections.addAll(allDescriptors, descriptor); + } + HighlightSeverity severity = profileToUse.getInspectionProfile().getErrorLevel(toolKey, containingFile).getSeverity(); + final HighlightInfoType infoType = + ProblemDescriptorUtil.highlightTypeFromDescriptor(descriptor, severity, SeverityRegistrar.getSeverityRegistrar(project)); + highlightTypes.add(infoType); + } + } + } + } + return new InspectionPassInfo(allDescriptors.toArray(new ResourceBundleEditorProblemDescriptor[allDescriptors.size()]), highlightTypes); + } + + public static class InspectionPassInfo { + private final ResourceBundleEditorProblemDescriptor[] myDescriptors; + private final SortedSet myHighlightTypes; + + public InspectionPassInfo(ResourceBundleEditorProblemDescriptor[] descriptors, SortedSet types) { + myDescriptors = descriptors; + myHighlightTypes = types; + } + + public ResourceBundleEditorProblemDescriptor[] getDescriptors() { + return myDescriptors; + } + + @Nullable + public TextAttributes getTextAttributes(EditorColorsScheme scheme) { + TextAttributes mixedAttributes = null; + for (HighlightInfoType type : myHighlightTypes) { + final TextAttributes current = scheme.getAttributes(type.getAttributesKey()); + if (mixedAttributes == null) { + mixedAttributes = current; + } else { + mixedAttributes = TextAttributes.merge(mixedAttributes, current); + } + } + return mixedAttributes; + } + } +} diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorProblemDescriptor.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorProblemDescriptor.java new file mode 100644 index 000000000000..c17bcb3c794f --- /dev/null +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorProblemDescriptor.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2015 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.lang.properties.editor.inspections; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.codeInspection.QuickFix; +import com.intellij.lang.annotation.ProblemGroup; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleEditorProblemDescriptor implements ProblemDescriptor { + private final ProblemHighlightType myHighlightType; + private final String myDescriptionTemplate; + private final QuickFix[] myFixes; + + public ResourceBundleEditorProblemDescriptor(final ProblemHighlightType type, String template, QuickFix... fixes) { + myHighlightType = type; + myDescriptionTemplate = template; + myFixes = fixes; + } + + @NotNull + public ProblemHighlightType getHighlightType() { + return myHighlightType; + } + + @NotNull + @Override + public String getDescriptionTemplate() { + return myDescriptionTemplate; + } + + @Nullable + @Override + public QuickFix[] getFixes() { + return myFixes; + } + + @Override + public PsiElement getPsiElement() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getStartElement() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getEndElement() { + throw new UnsupportedOperationException(); + } + + @Override + public TextRange getTextRangeInElement() { + throw new UnsupportedOperationException(); + } + + @Override + public int getLineNumber() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isAfterEndOfLine() { + throw new UnsupportedOperationException(); + } + + @Override + public void setTextAttributes(TextAttributesKey key) { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public ProblemGroup getProblemGroup() { + throw new UnsupportedOperationException(); + } + + @Override + public void setProblemGroup(@Nullable ProblemGroup problemGroup) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean showTooltip() { + throw new UnsupportedOperationException(); + } +} diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorRenderer.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorRenderer.java new file mode 100644 index 000000000000..643efc566be4 --- /dev/null +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/inspections/ResourceBundleEditorRenderer.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2015 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.lang.properties.editor.inspections; + +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.ide.util.treeView.NodeRenderer; +import com.intellij.ide.util.treeView.smartTree.TreeElement; +import com.intellij.ide.util.treeView.smartTree.TreeElementWrapper; +import com.intellij.lang.properties.editor.ResourceBundlePropertyStructureViewElement; +import com.intellij.navigation.ItemPresentation; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.ui.SimpleTextAttributes; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import java.awt.*; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleEditorRenderer extends NodeRenderer { + + @Override + public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + if (customize(value)) { + return; + } + super.customizeCellRenderer(tree, value, selected, expanded, leaf, row, hasFocus); + } + + private boolean customize(Object value) { + final Object userObject = ((DefaultMutableTreeNode)value).getUserObject(); + if (!(userObject instanceof TreeElementWrapper)) { + return false; + } + final TreeElement treeElement = ((TreeElementWrapper)userObject).getValue(); + if (treeElement == null) { + return false; + } + final ItemPresentation presentation = treeElement.getPresentation(); + if (presentation instanceof TextAttributesPresentation) { + final TextAttributesPresentation textAttributesPresentation = (TextAttributesPresentation)presentation; + final String text = textAttributesPresentation.getPresentableText(); + if (text != null) { + final SimpleTextAttributes attr = + SimpleTextAttributes.fromTextAttributes(textAttributesPresentation.getTextAttributes(getColorsScheme())); + append(text, new SimpleTextAttributes(attr.getBgColor(), attr.getFgColor(), attr.getWaveColor(), + attr.getStyle() | SimpleTextAttributes.STYLE_OPAQUE)); + return true; + } + } + return false; + } + + public interface TextAttributesPresentation extends ItemPresentation { + TextAttributes getTextAttributes(EditorColorsScheme colorsScheme); + } +} diff --git a/plugins/properties/src/META-INF/plugin.xml b/plugins/properties/src/META-INF/plugin.xml index 2f6150c4a7cb..3f5d08df9093 100644 --- a/plugins/properties/src/META-INF/plugin.xml +++ b/plugins/properties/src/META-INF/plugin.xml @@ -86,6 +86,10 @@ key="wrong.property.key.value.delimiter.inspection.display.name" groupKey="properties.files.inspection.group.display.name" enabledByDefault="true" level="WEAK WARNING" cleanupTool="true" implementationClass="com.intellij.codeInspection.WrongPropertyKeyValueDelimiterInspection"/> + @@ -107,13 +111,6 @@ - - - @@ -156,5 +153,9 @@ + + diff --git a/plugins/properties/src/com/intellij/lang/properties/ResourceBundleEditorConfigurable.java b/plugins/properties/src/com/intellij/lang/properties/ResourceBundleEditorConfigurable.java deleted file mode 100644 index 116bcb56e9de..000000000000 --- a/plugins/properties/src/com/intellij/lang/properties/ResourceBundleEditorConfigurable.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2000-2015 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.lang.properties; - -import com.intellij.lang.properties.editor.IgnoredPropertiesFilesSuffixesManager; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.options.BaseConfigurable; -import com.intellij.openapi.options.Configurable; -import com.intellij.openapi.options.ConfigurableProvider; -import com.intellij.openapi.options.ConfigurationException; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.ui.*; -import com.intellij.ui.components.JBList; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -/** - * @author Dmitry Batkovich - */ -public class ResourceBundleEditorConfigurable extends BaseConfigurable { - private final JPanel myPanel; - private final CollectionListModel mySuffixesModel; - private final IgnoredPropertiesFilesSuffixesManager mySuffixesManager; - - public ResourceBundleEditorConfigurable(@NotNull Project project) { - mySuffixesManager = IgnoredPropertiesFilesSuffixesManager.getInstance(project); - final JBList list = new JBList(); - final List suffixes = new ArrayList(mySuffixesManager.getIgnoredSuffixes()); - mySuffixesModel = new CollectionListModel(suffixes); - mySuffixesModel.sort(String.CASE_INSENSITIVE_ORDER); - list.setModel(mySuffixesModel); - myPanel = ToolbarDecorator.createDecorator(list).disableUpDownActions().setAddAction(new AnActionButtonRunnable() { - @Override - public void run(AnActionButton button) { - final String result = Messages.showInputDialog(CommonDataKeys.PROJECT.getData(button.getDataContext()), - "Suffixes to ignore (use comma to separate suffixes):", - "Add Ignored Suffixes", null); - if (result != null) { - final List suffixes = StringUtil.split(result, ","); - for (String suffix : suffixes) { - if (mySuffixesModel.getElementIndex(suffix) == -1) { - mySuffixesModel.add(suffix); - } - } - updateModifiedStatus(); - mySuffixesModel.sort(String.CASE_INSENSITIVE_ORDER); - } - } - }).setRemoveAction(new AnActionButtonRunnable() { - @Override - public void run(AnActionButton button) { - for (Object toDelete : list.getSelectedValues()) { - mySuffixesModel.remove((String)toDelete); - } - updateModifiedStatus(); - } - }).createPanel(); - list.setCellRenderer(new ColoredListCellRenderer() { - @Override - protected void customizeCellRenderer(JList list, String suffix, int index, boolean selected, boolean hasFocus) { - append(suffix); - final Locale locale = PropertiesUtil.getLocale("_" + suffix + ".properties"); - if (locale != PropertiesUtil.DEFAULT_LOCALE && PropertiesUtil.hasDefaultLanguage(locale)) { - append(" "); - append(PropertiesUtil.getPresentableLocale(locale), SimpleTextAttributes.GRAY_ATTRIBUTES); - } - } - }); - myPanel.setBorder(IdeBorderFactory.createTitledBorder("Ignored properties file suffixes:")); - } - - @Nls - @Override - public String getDisplayName() { - return "Resource Bundle Editor"; - } - - @Nullable - @Override - public String getHelpTopic() { - return null; - } - - @Nullable - @Override - public JComponent createComponent() { - return myPanel; - } - - @Override - public void apply() throws ConfigurationException { - mySuffixesManager.setSuffixes(mySuffixesModel.getItems()); - setModified(false); - } - - @Override - public void reset() { - mySuffixesModel.removeAll(); - for (String suffix : mySuffixesManager.getIgnoredSuffixes()) { - mySuffixesModel.add(suffix); - } - mySuffixesModel.sort(String.CASE_INSENSITIVE_ORDER); - setModified(false); - } - - @Override - public void disposeUIResources() { - } - - private void updateModifiedStatus() { - setModified(!ContainerUtil.newHashSet(mySuffixesModel.getItems()).equals(mySuffixesManager.getIgnoredSuffixes())); - } - - public static class Provider extends ConfigurableProvider { - private final Project myProject; - - public Provider(Project project) { - myProject = project; - } - - @Nullable - @Override - public Configurable createConfigurable() { - return new ResourceBundleEditorConfigurable(myProject); - } - } -} diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/IgnoreIncompletePropertyPropertiesFilesAction.java b/plugins/properties/src/com/intellij/lang/properties/editor/IgnoreIncompletePropertyPropertiesFilesAction.java deleted file mode 100644 index c4553a63725e..000000000000 --- a/plugins/properties/src/com/intellij/lang/properties/editor/IgnoreIncompletePropertyPropertiesFilesAction.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2000-2015 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.lang.properties.editor; - -import com.intellij.lang.properties.IProperty; -import com.intellij.lang.properties.PropertiesUtil; -import com.intellij.lang.properties.psi.PropertiesFile; -import com.intellij.lang.properties.psi.Property; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.PlatformDataKeys; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.psi.PsiElement; -import com.intellij.ui.CollectionListModel; -import com.intellij.ui.ToolbarDecorator; -import com.intellij.ui.components.JBList; -import com.intellij.util.NotNullFunction; -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.util.*; -import java.util.List; - -/** - * @author Dmitry Batkovich - */ -public class IgnoreIncompletePropertyPropertiesFilesAction extends AnAction { - private final static Logger LOG = Logger.getInstance(IgnoreIncompletePropertyPropertiesFilesAction.class); - - public IgnoreIncompletePropertyPropertiesFilesAction() { - super("Ignore Properties Files Without Translation"); - } - - @Override - public void actionPerformed(AnActionEvent e) { - final ResourceBundleEditor resourceBundleEditor = (ResourceBundleEditor)PlatformDataKeys.FILE_EDITOR.getData(e.getDataContext()); - LOG.assertTrue(resourceBundleEditor != null); - final Project project = getEventProject(e); - LOG.assertTrue(project != null); - - final Set properties = new HashSet(); - processSelectedIncompleteProperties(new Processor() { - @Override - public boolean process(IProperty property) { - properties.add(property.getKey()); - return true; - } - }, resourceBundleEditor, project); - - final IgnoredPropertiesFilesSuffixesManager suffixesManager = IgnoredPropertiesFilesSuffixesManager.getInstance(project); - final List allFilesWithoutTranslation = - suffixesManager.getPropertiesFilesWithoutTranslation(resourceBundleEditor.getResourceBundle(), properties); - if (allFilesWithoutTranslation.isEmpty()) { - return; - } - Collections.sort(allFilesWithoutTranslation, new Comparator() { - @Override - public int compare(PropertiesFile p1, PropertiesFile p2) { - return p1.getName().compareTo(p2.getName()); - } - }); - - final List suffixRepresentatives = - new IgnoredSuffixesDialog(allFilesWithoutTranslation, project).showAndGetSuffixesRepresentatives(); - if (suffixRepresentatives == null) { - return; - } - final List suffixesToIgnore = ContainerUtil.map(suffixRepresentatives, new NotNullFunction() { - @NotNull - @Override - public String fun(PropertiesFile propertiesFile) { - return PropertiesUtil.getSuffix(propertiesFile); - } - }); - if (!suffixesToIgnore.isEmpty()) { - suffixesManager.addSuffixes(suffixesToIgnore); - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - resourceBundleEditor.queueUpdateTree(); - } - }); - } - } - - private static class IgnoredSuffixesDialog extends DialogWrapper { - @NotNull private final List myPropertiesFiles; - private CollectionListModel myModel; - - protected IgnoredSuffixesDialog(@NotNull List propertiesFiles, @NotNull Project project) { - super(project); - myPropertiesFiles = propertiesFiles; - setTitle("Suffixes to Ignore:"); - init(); - } - - @SuppressWarnings("unchecked") - @Nullable - @Override - protected JComponent createCenterPanel() { - final JBList list = new JBList(); - myModel = new CollectionListModel(myPropertiesFiles); - list.setModel(myModel); - list.setCellRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - final JLabel label = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - label.setText(PropertiesUtil.getSuffix((PropertiesFile)value)); - return label; - } - }); - return ToolbarDecorator.createDecorator(list).disableUpDownActions().disableAddAction().createPanel(); - } - - @Nullable - public List showAndGetSuffixesRepresentatives() { - return showAndGet() ? myModel.getItems() : null; - } - } - - @Override - public void update(AnActionEvent e) { - final FileEditor fileEditor = PlatformDataKeys.FILE_EDITOR.getData(e.getDataContext()); - if (fileEditor instanceof ResourceBundleEditor) { - ResourceBundleEditor resourceBundleEditor = (ResourceBundleEditor)fileEditor; - final Project project = getEventProject(e); - if (project != null) { - if (!processSelectedIncompleteProperties(new Processor() { - @Override - public boolean process(IProperty property) { - return false; - } - }, resourceBundleEditor, project)) { - e.getPresentation().setEnabledAndVisible(true); - return; - } - } - } - e.getPresentation().setEnabledAndVisible(false); - } - - private static boolean processSelectedIncompleteProperties(final @NotNull Processor processor, - final @NotNull ResourceBundleEditor resourceBundleEditor, - final @NotNull Project project) { - final IgnoredPropertiesFilesSuffixesManager suffixesManager = IgnoredPropertiesFilesSuffixesManager.getInstance(project); - for (ResourceBundleEditorViewElement element : resourceBundleEditor.getSelectedElements()) { - final IProperty[] properties = element.getProperties(); - if (properties != null) { - for (IProperty property : properties) { - if (!suffixesManager.isPropertyComplete(resourceBundleEditor.getResourceBundle(), property.getKey()) && !processor.process(property)) { - return false; - } - } - } - } - return true; - } -} diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java index e081105ee1ec..32f676a27630 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java @@ -34,6 +34,7 @@ import com.intellij.lang.properties.IProperty; import com.intellij.lang.properties.PropertiesImplUtil; import com.intellij.lang.properties.PropertiesUtil; import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.editor.inspections.incomplete.IncompletePropertyInspection; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.lang.properties.psi.PropertiesResourceBundleUtil; import com.intellij.openapi.actionSystem.*; @@ -111,7 +112,6 @@ public class ResourceBundleEditor extends UserDataHolderBase implements Document private final Set myBackSlashPressed = new THashSet(); private final Alarm mySelectionChangeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); private final PropertiesAnchorizer myPropertiesAnchorizer; - private final IgnoredPropertiesFilesSuffixesManager.SuffixesListener mySuffixesListener; private JPanel myValuesPanel; private JPanel myStructureViewPanel; @@ -201,14 +201,6 @@ public class ResourceBundleEditor extends UserDataHolderBase implements Document onSelectionChanged(event); } }); - - mySuffixesListener = new IgnoredPropertiesFilesSuffixesManager.SuffixesListener() { - @Override - public void suffixesChanged() { - recreateEditorsPanel(); - } - }; - IgnoredPropertiesFilesSuffixesManager.getInstance(myProject).addListener(mySuffixesListener); } public ResourceBundle getResourceBundle() { @@ -406,35 +398,6 @@ public class ResourceBundleEditor extends UserDataHolderBase implements Document myValuesPanel.add(myNoPropertySelectedPanel, NO_PROPERTY_SELECTED); final List propertiesFiles = myResourceBundle.getPropertiesFiles(); - final IgnoredPropertiesFilesSuffixesManager suffixesManager = - IgnoredPropertiesFilesSuffixesManager.getInstance(myResourceBundle.getProject()); - if (!suffixesManager.getIgnoredSuffixes().isEmpty()) { - final List initialOrder = new ArrayList(propertiesFiles); - final PropertiesFile defaultPropertiesFile = myResourceBundle.getDefaultPropertiesFile(); - Collections.sort(propertiesFiles, new Comparator() { - - @Override - public int compare(PropertiesFile p1, PropertiesFile p2) { - if (p1.equals(defaultPropertiesFile)) { - return -1; - } - if (p2.equals(defaultPropertiesFile)) { - return 1; - } - final boolean isIgnored1 = suffixesManager.getIgnoredSuffixes().contains(PropertiesUtil.getSuffix(p1)); - final boolean isIgnored2 = suffixesManager.getIgnoredSuffixes().contains(PropertiesUtil.getSuffix(p2)); - if (isIgnored1 != isIgnored2) { - if (isIgnored1) { - return 1; - } - else { - return -1; - } - } - return initialOrder.indexOf(p1) - initialOrder.indexOf(p2); - } - }); - } GridBagConstraints gc = new GridBagConstraints(0, 0, 0, 0, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0); @@ -733,12 +696,12 @@ public class ResourceBundleEditor extends UserDataHolderBase implements Document final String currentKey = selectedProperty.getKey(); final int idx = keysOrder.indexOf(currentKey); LOG.assertTrue(idx != -1); - final IgnoredPropertiesFilesSuffixesManager - ignoredPropertiesFilesSuffixesManager = IgnoredPropertiesFilesSuffixesManager.getInstance(myProject); + final IncompletePropertyInspection incompletePropertyInspection = + IncompletePropertyInspection.getInstance(myResourceBundle.getDefaultPropertiesFile().getContainingFile()); for (int i = 1; i < keysOrder.size(); i++) { int trimmedIndex = (i + idx) % keysOrder.size(); final String key = keysOrder.get(trimmedIndex); - if (!ignoredPropertiesFilesSuffixesManager.isPropertyComplete(myResourceBundle, key)) { + if (!incompletePropertyInspection.isPropertyComplete(key, myResourceBundle)) { selectProperty(key); return; } @@ -894,7 +857,6 @@ public class ResourceBundleEditor extends UserDataHolderBase implements Document } } } - IgnoredPropertiesFilesSuffixesManager.getInstance(myProject).removeListener(mySuffixesListener); VirtualFileManager.getInstance().removeVirtualFileListener(myVfsListener); myDisposed = true; Disposer.dispose(myStructureViewComponent); @@ -910,10 +872,6 @@ public class ResourceBundleEditor extends UserDataHolderBase implements Document myEditors.clear(); } - public void queueUpdateTree() { - myStructureViewComponent.getTreeBuilder().queueUpdate(); - } - public void setKeepEmptyProperties(boolean keepEmptyProperties) { myKeepEmptyProperties = keepEmptyProperties; } diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorShowQuickFixesAction.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorShowQuickFixesAction.java new file mode 100644 index 000000000000..8cd69205f18f --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorShowQuickFixesAction.java @@ -0,0 +1,135 @@ +/* + * Copyright 2000-2015 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.lang.properties.editor; + +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.impl.IntentionListStep; +import com.intellij.codeInspection.QuickFix; +import com.intellij.icons.AllIcons; +import com.intellij.lang.properties.editor.inspections.ResourceBundleEditorProblemDescriptor; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.ui.popup.PopupFactoryImpl; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleEditorShowQuickFixesAction extends AnAction { + private final static Logger LOG = Logger.getInstance(ResourceBundleEditorShowQuickFixesAction.class); + + @Override + public void actionPerformed(AnActionEvent e) { + final ResourceBundleEditor editor = getEditor(e); + LOG.assertTrue(editor != null); + final ResourceBundlePropertyStructureViewElement element = (ResourceBundlePropertyStructureViewElement)editor.getSelectedElementIfOnlyOne(); + LOG.assertTrue(element != null); + + final PsiFile file = editor.getResourceBundle().getDefaultPropertiesFile().getContainingFile(); + final ShowIntentionsPass.IntentionsInfo intentions = new ShowIntentionsPass.IntentionsInfo(); + + boolean isQuickFixListEmpty = true; + ResourceBundleEditorProblemDescriptor[] descriptors = element.getProblemDescriptors(); + for (ResourceBundleEditorProblemDescriptor d : descriptors) { + QuickFix[] fixes = d.getFixes(); + if (fixes != null) { + for (int i = 0; i < fixes.length; i++) { + intentions.inspectionFixesToShow.add(new HighlightInfo.IntentionActionDescriptor(new RBEQuickFixWrapper(d, i), + AllIcons.Actions.IntentionBulb)); + isQuickFixListEmpty = false; + } + } + } + + if (isQuickFixListEmpty) { + return; + } + + final Project project = e.getProject(); + LOG.assertTrue(project != null); + PopupFactoryImpl + .getInstance() + .createListPopup(new IntentionListStep(null, intentions, null, file, project)) + .showInBestPositionFor(e.getDataContext()); + } + + @Override + public void update(AnActionEvent e) { + final ResourceBundleEditor editor = getEditor(e); + e.getPresentation().setEnabledAndVisible(editor != null && + editor.getSelectedElementIfOnlyOne() instanceof ResourceBundlePropertyStructureViewElement); + } + + private ResourceBundleEditor getEditor(AnActionEvent e) { + final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(e.getDataContext()); + return editor instanceof ResourceBundleEditor ? (ResourceBundleEditor)editor : null; + } + + private static class RBEQuickFixWrapper implements IntentionAction { + private final ResourceBundleEditorProblemDescriptor myDescriptor; + private final int myIndex; + + private RBEQuickFixWrapper(ResourceBundleEditorProblemDescriptor descriptor, int index) { + myDescriptor = descriptor; + myIndex = index; + } + + @Nls + @NotNull + @Override + public String getText() { + return getFamilyName(); + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return getQuickFix().getFamilyName(); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return true; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + getQuickFix().applyFix(project, myDescriptor); + } + + @Override + public boolean startInWriteAction() { + return false; + } + + private QuickFix getQuickFix() { + return myDescriptor.getFixes()[myIndex]; + } + } + +} + diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java index 38cd0c3685fd..511edcca4aca 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java @@ -21,9 +21,9 @@ import com.intellij.ide.DeleteProvider; import com.intellij.ide.actions.ContextHelpAction; import com.intellij.lang.properties.IProperty; import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.editor.inspections.ResourceBundleEditorRenderer; import com.intellij.lang.properties.projectView.ResourceBundleDeleteProvider; import com.intellij.lang.properties.psi.PropertiesFile; -import com.intellij.lang.properties.psi.Property; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.ide.CopyPasteManager; @@ -64,6 +64,7 @@ public class ResourceBundleStructureViewComponent extends PropertiesGroupingStru super(resourceBundle.getProject(), editor, new ResourceBundleStructureViewModel(resourceBundle, anchorizer)); myResourceBundle = resourceBundle; tunePopupActionGroup(); + getTree().setCellRenderer(new ResourceBundleEditorRenderer()); } @Override @@ -83,7 +84,6 @@ public class ResourceBundleStructureViewComponent extends PropertiesGroupingStru final DefaultActionGroup propertiesPopupGroup = new DefaultActionGroup(); propertiesPopupGroup.copyFromGroup((DefaultActionGroup)ActionManager.getInstance().getAction(IdeActions.GROUP_STRUCTURE_VIEW_POPUP)); propertiesPopupGroup.add(Separator.getInstance(), Constraints.FIRST); - propertiesPopupGroup.add(new IgnoreIncompletePropertyPropertiesFilesAction(), Constraints.FIRST); propertiesPopupGroup.add(new NewPropertyAction(true), Constraints.FIRST); PopupHandler.installPopupHandler(getTree(), propertiesPopupGroup, IdeActions.GROUP_STRUCTURE_VIEW_POPUP, ActionManager.getInstance()); } diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/inspections/incomplete/IncompletePropertyInspection.java b/plugins/properties/src/com/intellij/lang/properties/editor/inspections/incomplete/IncompletePropertyInspection.java new file mode 100644 index 000000000000..e7ab1388e421 --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/editor/inspections/incomplete/IncompletePropertyInspection.java @@ -0,0 +1,202 @@ +/* + * Copyright 2000-2015 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.lang.properties.editor.inspections.incomplete; + +import com.intellij.codeInspection.InspectionProfile; +import com.intellij.codeInspection.ModifiableModel; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.codeInspection.QuickFix; +import com.intellij.codeInspection.ex.DisableInspectionToolAction; +import com.intellij.lang.properties.*; +import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.editor.inspections.ResourceBundleEditorInspection; +import com.intellij.lang.properties.editor.inspections.ResourceBundleEditorProblemDescriptor; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.SmartPointerManager; +import com.intellij.psi.SmartPsiElementPointer; +import com.intellij.util.Consumer; +import com.intellij.util.Function; +import com.intellij.util.containers.*; +import org.jdom.Element; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.*; +import java.util.List; + +/** + * @author Dmitry Batkovich + */ +public class IncompletePropertyInspection extends ResourceBundleEditorInspection { + private static final String SUFFIXES_TAG_NAME = "suffixes"; + private static final String TOOL_KEY = "IncompleteProperty"; + + SortedSet mySuffixes = new TreeSet(); + + @Nullable + @Override + public JComponent createOptionsPanel() { + return new IncompletePropertyInspectionOptionsPanel(mySuffixes).buildPanel(); + } + + @Override + public void readSettings(@NotNull Element node) throws InvalidDataException { + mySuffixes.clear(); + final Element element = node.getChild(SUFFIXES_TAG_NAME); + if (element != null) { + mySuffixes.addAll(StringUtil.split(element.getText(), ",")); + } + } + + @Override + public void writeSettings(@NotNull Element node) throws WriteExternalException { + if (!mySuffixes.isEmpty()) { + node.addContent(new Element(SUFFIXES_TAG_NAME).setText(StringUtil.join(mySuffixes, ","))); + } + } + + @Nullable + @Override + public ResourceBundleEditorProblemDescriptor[] checkPropertyGroup(@NotNull List properties, @NotNull ResourceBundle resourceBundle) { + return !isPropertyComplete(properties, resourceBundle) + ? new ResourceBundleEditorProblemDescriptor[] {new ResourceBundleEditorProblemDescriptor(ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + PropertiesBundle.message("incomplete.property.inspection.description", + properties.get(0).getName()), + new IgnoreLocalesQuickFix(properties.get(0), resourceBundle))} + : null; + } + + @NotNull + public static IncompletePropertyInspection getInstance(PsiElement element) { + final InspectionProjectProfileManager profileManager = InspectionProjectProfileManager.getInstance(element.getProject()); + InspectionProfile inspectionProfile = profileManager.getInspectionProfile(); + return (IncompletePropertyInspection) inspectionProfile.getUnwrappedTool(TOOL_KEY, element); + } + + private static class IgnoreLocalesQuickFix implements QuickFix { + private final ResourceBundle myResourceBundle; + private final SmartPsiElementPointer myElementPointer; + + public IgnoreLocalesQuickFix(IProperty property, ResourceBundle bundle) { + myElementPointer = SmartPointerManager.getInstance(bundle.getProject()).createSmartPsiElementPointer(property.getPsiElement()); + myResourceBundle = bundle; + } + + @Nls + @NotNull + @Override + public String getName() { + return getFamilyName(); + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return PropertiesBundle.message("incomplete.property.quick.fix.name"); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ResourceBundleEditorProblemDescriptor descriptor) { + final PsiElement element = myElementPointer.getElement(); + if (element == null) { + return; + } + final IProperty property = PropertiesImplUtil.getProperty(element); + if (property == null) { + return; + } + + final IncompletePropertyInspection inspection = getInstance(element); + final List allFilesWithoutTranslation = inspection.getPropertiesFilesWithoutTranslation(myResourceBundle, property.getKey()); + + if (allFilesWithoutTranslation.isEmpty()) { + return; + } + + + final TreeSet suffixesToIgnore = new TreeSet(ContainerUtil.map(allFilesWithoutTranslation, + new Function() { + @Override + public String fun(PropertiesFile file) { + return PropertiesUtil.getSuffix(file); + } + })); + if (new IncompletePropertyInspectionOptionsPanel(suffixesToIgnore).showDialogAndGet(project)) { + DisableInspectionToolAction.modifyAndCommitProjectProfile(new Consumer() { + @Override + public void consume(ModifiableModel modifiableModel) { + ((IncompletePropertyInspection)modifiableModel.getInspectionTool(TOOL_KEY, element).getTool()).addSuffixes(suffixesToIgnore); + } + }, project); + } + } + } + + public boolean isPropertyComplete(final String key, final ResourceBundle resourceBundle) { + return isPropertyComplete(ContainerUtil.mapNotNull(resourceBundle.getPropertiesFiles(), new Function() { + @Override + public IProperty fun(PropertiesFile file) { + return file.findPropertyByKey(key); + } + }), resourceBundle); + } + + private boolean isPropertyComplete(final List properties, final ResourceBundle resourceBundle) { + final Set existed = ContainerUtil.map2Set(properties, new Function() { + + @Override + public PropertiesFile fun(IProperty property) { + return property.getPropertiesFile(); + } + }); + for (PropertiesFile file : resourceBundle.getPropertiesFiles()) { + if (!existed.contains(file) && !getIgnoredSuffixes().contains(PropertiesUtil.getSuffix(file))) { + return false; + } + } + return true; + } + + public Set getIgnoredSuffixes() { + return mySuffixes; + } + + public List getPropertiesFilesWithoutTranslation(final ResourceBundle resourceBundle, final String key) { + final PropertiesFile defaultPropertiesFile = resourceBundle.getDefaultPropertiesFile(); + return ContainerUtil.filter(resourceBundle.getPropertiesFiles(), new Condition() { + @Override + public boolean value(PropertiesFile propertiesFile) { + return !defaultPropertiesFile.equals(propertiesFile) && + propertiesFile.findPropertyByKey(key) == null && + !getIgnoredSuffixes().contains(PropertiesUtil.getSuffix(propertiesFile)); + } + }); + } + + public void addSuffixes(Collection suffixes) { + mySuffixes.addAll(suffixes); + } +} diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/inspections/incomplete/IncompletePropertyInspectionOptionsPanel.java b/plugins/properties/src/com/intellij/lang/properties/editor/inspections/incomplete/IncompletePropertyInspectionOptionsPanel.java new file mode 100644 index 000000000000..501eef6baf8a --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/editor/inspections/incomplete/IncompletePropertyInspectionOptionsPanel.java @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2015 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.lang.properties.editor.inspections.incomplete; + +import com.intellij.lang.properties.PropertiesUtil; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.*; +import com.intellij.ui.components.JBList; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.Locale; +import java.util.SortedSet; + +/** + * @author Dmitry Batkovich + */ +public class IncompletePropertyInspectionOptionsPanel { + + private final SortedSet mySuffixes; + private final JBList myList; + + public IncompletePropertyInspectionOptionsPanel(SortedSet suffixes) { + mySuffixes = suffixes; + myList = new JBList(new MyListModel()); + } + + public JPanel buildPanel() { + JPanel panel = ToolbarDecorator + .createDecorator(myList) + .setPanelBorder(IdeBorderFactory.createTitledBorder("Ignored suffixes")) + .disableUpDownActions() + .setAddAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + final String result = Messages.showInputDialog(CommonDataKeys.PROJECT.getData(button.getDataContext()), + "Suffixes to ignore (use comma to separate suffixes):", + "Add Ignored Suffixes", null); + if (result != null) { + mySuffixes.addAll(StringUtil.split(result, ",")); + changed(); + } + } + }).setRemoveAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + for (Object v : myList.getSelectedValues()) { + mySuffixes.remove(v); + } + changed(); + } + }).createPanel(); + myList.setCellRenderer(new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(JList list, String suffix, int index, boolean selected, boolean hasFocus) { + append(suffix); + final Locale locale = PropertiesUtil.getLocale("_" + suffix + ".properties"); + if (locale != PropertiesUtil.DEFAULT_LOCALE && PropertiesUtil.hasDefaultLanguage(locale)) { + append(" "); + append(PropertiesUtil.getPresentableLocale(locale), SimpleTextAttributes.GRAY_ATTRIBUTES); + } + } + }); + return panel; + } + + public boolean showDialogAndGet(Project project) { + return new DialogWrapper(project) { + { + init(); + setTitle("Locales to Ignore"); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return buildPanel(); + } + }.showAndGet(); + } + + private void changed() { + ((MyListModel)myList.getModel()).modified(); + } + + private class MyListModel extends AbstractListModel { + public int getSize() { + return mySuffixes.size(); + } + + public Object getElementAt(int index) { + return mySuffixes.toArray(new String[mySuffixes.size()])[index]; + } + + public void modified() { + fireContentsChanged(this, -1, -1); + } + } +} diff --git a/plugins/properties/testSrc/com/intellij/lang/properties/IgnoredPropertiesFilesSuffixesTest.java b/plugins/properties/testSrc/com/intellij/lang/properties/IgnoredPropertiesFilesSuffixesTest.java index 380799899327..35fd35cc496f 100644 --- a/plugins/properties/testSrc/com/intellij/lang/properties/IgnoredPropertiesFilesSuffixesTest.java +++ b/plugins/properties/testSrc/com/intellij/lang/properties/IgnoredPropertiesFilesSuffixesTest.java @@ -15,7 +15,7 @@ */ package com.intellij.lang.properties; -import com.intellij.lang.properties.editor.IgnoredPropertiesFilesSuffixesManager; +import com.intellij.lang.properties.editor.inspections.incomplete.IncompletePropertyInspection; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.psi.PsiFile; import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; @@ -27,6 +27,12 @@ import java.util.Collections; */ public class IgnoredPropertiesFilesSuffixesTest extends LightPlatformCodeInsightFixtureTestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.enableInspections(IncompletePropertyInspection.class); + } + public void testPropertyIsComplete() { myFixture.addFileToProject("p.properties", "key=value"); myFixture.addFileToProject("p_en.properties", "key=value eng"); @@ -35,9 +41,9 @@ public class IgnoredPropertiesFilesSuffixesTest extends LightPlatformCodeInsight assertNotNull(propertiesFile); final ResourceBundle resourceBundle = propertiesFile.getResourceBundle(); assertSize(3, resourceBundle.getPropertiesFiles()); - final IgnoredPropertiesFilesSuffixesManager suffixesManager = IgnoredPropertiesFilesSuffixesManager.getInstance(getProject()); - suffixesManager.addSuffixes(Collections.singleton("ru")); - assertTrue(suffixesManager.isPropertyComplete(resourceBundle, "key")); + final IncompletePropertyInspection incompletePropertyInspection = IncompletePropertyInspection.getInstance(propertiesFile.getContainingFile()); + incompletePropertyInspection.addSuffixes(Collections.singleton("ru")); + assertTrue(incompletePropertyInspection.isPropertyComplete("key", resourceBundle)); } public void testPropertyIsComplete2() { @@ -48,8 +54,8 @@ public class IgnoredPropertiesFilesSuffixesTest extends LightPlatformCodeInsight assertNotNull(propertiesFile); final ResourceBundle resourceBundle = propertiesFile.getResourceBundle(); assertSize(3, resourceBundle.getPropertiesFiles()); - final IgnoredPropertiesFilesSuffixesManager suffixesManager = IgnoredPropertiesFilesSuffixesManager.getInstance(getProject()); - assertTrue(suffixesManager.isPropertyComplete(resourceBundle, "key")); + final IncompletePropertyInspection incompletePropertyInspection = IncompletePropertyInspection.getInstance(propertiesFile.getContainingFile()); + assertTrue(incompletePropertyInspection.isPropertyComplete("key", resourceBundle)); } public void testPropertyIsIncomplete() { @@ -61,9 +67,9 @@ public class IgnoredPropertiesFilesSuffixesTest extends LightPlatformCodeInsight assertNotNull(propertiesFile); final ResourceBundle resourceBundle = propertiesFile.getResourceBundle(); assertSize(4, resourceBundle.getPropertiesFiles()); - final IgnoredPropertiesFilesSuffixesManager suffixesManager = IgnoredPropertiesFilesSuffixesManager.getInstance(getProject()); - suffixesManager.addSuffixes(Collections.singleton("ru")); - assertFalse(suffixesManager.isPropertyComplete(resourceBundle, "key")); + final IncompletePropertyInspection incompletePropertyInspection = IncompletePropertyInspection.getInstance(propertiesFile.getContainingFile()); + incompletePropertyInspection.addSuffixes(Collections.singleton("ru")); + assertFalse(incompletePropertyInspection.isPropertyComplete("key", resourceBundle)); } public void testPropertyIsIncomplete2() { @@ -76,14 +82,8 @@ public class IgnoredPropertiesFilesSuffixesTest extends LightPlatformCodeInsight assertNotNull(propertiesFile); final ResourceBundle resourceBundle = propertiesFile.getResourceBundle(); assertSize(5, resourceBundle.getPropertiesFiles()); - final IgnoredPropertiesFilesSuffixesManager suffixesManager = IgnoredPropertiesFilesSuffixesManager.getInstance(getProject()); - suffixesManager.addSuffixes(Collections.singleton("en")); - assertFalse(suffixesManager.isPropertyComplete(resourceBundle, "key")); - } - - @Override - public void tearDown() throws Exception { - IgnoredPropertiesFilesSuffixesManager.getInstance(getProject()).setSuffixes(Collections.emptySet()); - super.tearDown(); + final IncompletePropertyInspection incompletePropertyInspection = IncompletePropertyInspection.getInstance(propertiesFile.getContainingFile()); + incompletePropertyInspection.addSuffixes(Collections.singleton("en")); + assertFalse(incompletePropertyInspection.isPropertyComplete("key", resourceBundle)); } } diff --git a/resources-en/src/inspectionDescriptions/IncompleteProperty.html b/resources-en/src/inspectionDescriptions/IncompleteProperty.html new file mode 100644 index 000000000000..11844d34906b --- /dev/null +++ b/resources-en/src/inspectionDescriptions/IncompleteProperty.html @@ -0,0 +1,20 @@ + + + +Inspection detects property keys which have no translations in part of resource bundle properties files. + + \ No newline at end of file