From cb4c2b5db1cd9ea25d617bf89a19db14b86b2be1 Mon Sep 17 00:00:00 2001 From: hsz Date: Wed, 15 Feb 2017 23:44:18 +0100 Subject: [PATCH] #319 - Inform about editing ignored file --- resources/META-INF/plugin.xml | 6 +- resources/messages/IgnoreBundle.properties | 3 + ...dUnversionedFilesNotificationProvider.java | 3 +- .../IgnoredEditingNotificationProvider.java | 126 ++++++++++++++++++ .../MissingGitignoreNotificationProvider.java | 3 +- .../gitignore/settings/IgnoreSettings.java | 35 ++++- .../settings/IgnoreSettingsConfigurable.java | 3 + .../gitignore/ui/IgnoreSettingsPanel.form | 21 ++- .../gitignore/ui/IgnoreSettingsPanel.java | 21 +++ .../hsz/idea/gitignore/util/Properties.java | 73 ++++++++-- 10 files changed, 262 insertions(+), 32 deletions(-) create mode 100644 src/mobi/hsz/idea/gitignore/daemon/IgnoredEditingNotificationProvider.java diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index d131a709..55719aab 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -137,10 +137,12 @@ language="Ignore" implementationClass="mobi.hsz.idea.gitignore.codeInsight.SyntaxCompletionContributor"/> - + + diff --git a/resources/messages/IgnoreBundle.properties b/resources/messages/IgnoreBundle.properties index fe8626b4..f8bcf6d2 100644 --- a/resources/messages/IgnoreBundle.properties +++ b/resources/messages/IgnoreBundle.properties @@ -49,10 +49,12 @@ quick.fix.relative.entry=Remove relative part quick.fix.syntax.entry=Suggest correct value daemon.lineMarker.directory=Directory entry +daemon.ignoredEditing=You are editing file which is ignored daemon.missingGitignore=Missing .gitignore file in GIT project daemon.missingGitignore.create=Create .gitignore daemon.addUnversionedFiles=Would you like to add unversioned files to the .gitignore file? daemon.addUnversionedFiles.create=Add unversioned files +daemon.ok=OK daemon.cancel=Cancel dialog.generator.title=Ignore File Generator @@ -117,6 +119,7 @@ settings.general.insertAtCursor=Insert new ignore entries at cursor &position settings.general.addUnversionedFiles=Suggest to add &unversioned files (Git only) settings.general.unignoreFiles=Enable unignore files group (add entries prefixed with !) settings.general.informTrackedIgnored=Inform about ignored files that are still tracked (Git only) +settings.general.notifyIgnoredEditing=Inform about editing ignored file settings.general.donate=Donate me with: settings.userTemplates=User templates settings.userTemplates.noTemplateSelected=No template is selected. diff --git a/src/mobi/hsz/idea/gitignore/daemon/AddUnversionedFilesNotificationProvider.java b/src/mobi/hsz/idea/gitignore/daemon/AddUnversionedFilesNotificationProvider.java index 1f813d51..fe9a9c96 100644 --- a/src/mobi/hsz/idea/gitignore/daemon/AddUnversionedFilesNotificationProvider.java +++ b/src/mobi/hsz/idea/gitignore/daemon/AddUnversionedFilesNotificationProvider.java @@ -60,8 +60,7 @@ public class AddUnversionedFilesNotificationProvider extends EditorNotifications.Provider { /** Notification key. */ @NotNull - private static final Key KEY = - Key.create(IgnoreBundle.message("daemon.missingGitignore.create")); + private static final Key KEY = Key.create("AddUnversionedFilesNotificationProvider"); /** Current project. */ @NotNull diff --git a/src/mobi/hsz/idea/gitignore/daemon/IgnoredEditingNotificationProvider.java b/src/mobi/hsz/idea/gitignore/daemon/IgnoredEditingNotificationProvider.java new file mode 100644 index 00000000..5d68c28d --- /dev/null +++ b/src/mobi/hsz/idea/gitignore/daemon/IgnoredEditingNotificationProvider.java @@ -0,0 +1,126 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 hsz Jakub Chrzanowski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package mobi.hsz.idea.gitignore.daemon; + +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.EditorNotificationPanel; +import com.intellij.ui.EditorNotifications; +import mobi.hsz.idea.gitignore.IgnoreBundle; +import mobi.hsz.idea.gitignore.IgnoreManager; +import mobi.hsz.idea.gitignore.settings.IgnoreSettings; +import mobi.hsz.idea.gitignore.util.Icons; +import mobi.hsz.idea.gitignore.util.Properties; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Editor notification provider that informs about the attempt of the ignored file modification. + * + * @author Jakub Chrzanowski + * @since 1.8 + */ +public class IgnoredEditingNotificationProvider extends EditorNotifications.Provider { + /** Notification key. */ + @NotNull + private static final Key KEY = Key.create("IgnoredEditingNotificationProvider"); + + /** Current project. */ + @NotNull + private final Project project; + + /** Notifications component. */ + @NotNull + private final EditorNotifications notifications; + + /** Plugin settings holder. */ + @NotNull + private final IgnoreSettings settings; + + /** {@link IgnoreManager} instance. */ + @NotNull + private final IgnoreManager manager; + + /** + * Builds a new instance of {@link IgnoredEditingNotificationProvider}. + * + * @param project current project + * @param notifications notifications component + */ + public IgnoredEditingNotificationProvider(@NotNull Project project, @NotNull EditorNotifications notifications) { + this.project = project; + this.notifications = notifications; + this.settings = IgnoreSettings.getInstance(); + this.manager = IgnoreManager.getInstance(project); + } + + /** + * Gets notification key. + * + * @return notification key + */ + @NotNull + @Override + public Key getKey() { + return KEY; + } + + /** + * Creates notification panel for given file and checks if is allowed to show the notification. + * + * @param file current file + * @param fileEditor current file editor + * @return created notification panel + */ + @Nullable + @Override + public EditorNotificationPanel createNotificationPanel(@NotNull final VirtualFile file, + @NotNull FileEditor fileEditor) { + if (!settings.isNotifyIgnoredEditing() || !manager.isFileIgnored(file) || + Properties.isDismissedIgnoredEditingNotification(project, file)) { + return null; + } + + final EditorNotificationPanel panel = new EditorNotificationPanel(); + + panel.setText(IgnoreBundle.message("daemon.ignoredEditing")); + panel.createActionLabel(IgnoreBundle.message("daemon.ok"), new Runnable() { + @Override + public void run() { + Properties.setDismissedIgnoredEditingNotification(project, file); + notifications.updateAllNotifications(); + } + }); + + try { // ignore if older SDK does not support panel icon + panel.icon(Icons.IGNORE); + } catch (NoSuchMethodError ignored) { + } + + return panel; + } +} diff --git a/src/mobi/hsz/idea/gitignore/daemon/MissingGitignoreNotificationProvider.java b/src/mobi/hsz/idea/gitignore/daemon/MissingGitignoreNotificationProvider.java index 11e0be03..edfdc028 100644 --- a/src/mobi/hsz/idea/gitignore/daemon/MissingGitignoreNotificationProvider.java +++ b/src/mobi/hsz/idea/gitignore/daemon/MissingGitignoreNotificationProvider.java @@ -57,8 +57,7 @@ public class MissingGitignoreNotificationProvider extends EditorNotifications.Provider { /** Notification key. */ @NotNull - private static final Key KEY = - Key.create(IgnoreBundle.message("daemon.missingGitignore.create")); + private static final Key KEY = Key.create("MissingGitignoreNotificationProvider"); /** Current project. */ @NotNull diff --git a/src/mobi/hsz/idea/gitignore/settings/IgnoreSettings.java b/src/mobi/hsz/idea/gitignore/settings/IgnoreSettings.java index 388f5f9b..497c9950 100644 --- a/src/mobi/hsz/idea/gitignore/settings/IgnoreSettings.java +++ b/src/mobi/hsz/idea/gitignore/settings/IgnoreSettings.java @@ -62,7 +62,8 @@ public enum KEY { OUTER_IGNORE_RULES("outerIgnoreRules"), OUTER_IGNORE_WRAPPER_HEIGHT("outerIgnoreWrapperHeight"), INSERT_AT_CURSOR("insertAtCursor"), ADD_UNVERSIONED_FILES("addUnversionedFiles"), VERSION("version"), STARRED_TEMPLATES("starredTemplates"), UNIGNORE_ACTIONS("unignoreActions"), - HIDE_IGNORED_FILES("hideIgnoredFiles"), INFORM_TRACKED_IGNORED("informTrackedIgnored"); + HIDE_IGNORED_FILES("hideIgnoredFiles"), INFORM_TRACKED_IGNORED("informTrackedIgnored"), + NOTIFY_IGNORED_EDITING("notifyIgnoredEditing"); private final String key; @@ -113,6 +114,9 @@ public String toString() { /** Inform user about the tracked and ignored files in the project. */ private boolean informTrackedIgnored = true; + /** Shows notification about editing ignored file. */ + private boolean notifyIgnoredEditing = true; + /** Starred templates. */ @NotNull private final List starredTemplates = ContainerUtil.newArrayList(); @@ -164,6 +168,7 @@ public Element getState() { element.setAttribute(KEY.UNIGNORE_ACTIONS.toString(), Boolean.toString(unignoreActions)); element.setAttribute(KEY.HIDE_IGNORED_FILES.toString(), Boolean.toString(hideIgnoredFiles)); element.setAttribute(KEY.INFORM_TRACKED_IGNORED.toString(), Boolean.toString(informTrackedIgnored)); + element.setAttribute(KEY.NOTIFY_IGNORED_EDITING.toString(), Boolean.toString(notifyIgnoredEditing)); Element languagesElement = new Element(KEY.LANGUAGES.toString()); for (Map.Entry> entry : @@ -252,6 +257,11 @@ public void loadState(Element element) { informTrackedIgnored = Boolean.parseBoolean(value); } + value = element.getAttributeValue(KEY.NOTIFY_IGNORED_EDITING.toString()); + if (value != null) { + notifyIgnoredEditing = Boolean.parseBoolean(value); + } + Element languagesElement = element.getChild(KEY.LANGUAGES.toString()); if (languagesElement != null) { for (Element languageElement : languagesElement.getChildren()) { @@ -430,15 +440,34 @@ public boolean isInformTrackedIgnored() { } /** - * Sets value for informing user about the tracked and ignored files in the project. + * Sets notification about editing ignored file status to enabled or disabled. * - * @param informTrackedIgnored inform about files + * @param informTrackedIgnored show or hide notification */ public void setInformTrackedIgnored(boolean informTrackedIgnored) { this.notifyOnChange(KEY.INFORM_TRACKED_IGNORED, this.informTrackedIgnored, informTrackedIgnored); this.informTrackedIgnored = informTrackedIgnored; } + /** + * Checks if notifications about editing ignored file are enabled + * + * @return true if notification are enabled + */ + public boolean isNotifyIgnoredEditing() { + return notifyIgnoredEditing; + } + + /** + * Sets value for informing user about the tracked and ignored files in the project. + * + * @param notifyIgnoredEditing inform about files + */ + public void setNotifyIgnoredEditing(boolean notifyIgnoredEditing) { + this.notifyOnChange(KEY.NOTIFY_IGNORED_EDITING, this.notifyIgnoredEditing, notifyIgnoredEditing); + this.notifyIgnoredEditing = notifyIgnoredEditing; + } + /** * Returns the height of the outer ignore file wrapper panel. * diff --git a/src/mobi/hsz/idea/gitignore/settings/IgnoreSettingsConfigurable.java b/src/mobi/hsz/idea/gitignore/settings/IgnoreSettingsConfigurable.java index 2b6fac21..726ca068 100644 --- a/src/mobi/hsz/idea/gitignore/settings/IgnoreSettingsConfigurable.java +++ b/src/mobi/hsz/idea/gitignore/settings/IgnoreSettingsConfigurable.java @@ -108,6 +108,7 @@ public boolean isModified() { || !Comparing.equal(settings.isAddUnversionedFiles(), settingsPanel.isAddUnversionedFiles()) || !Comparing.equal(settings.isUnignoreActions(), settingsPanel.isUnignoreActions()) || !Comparing.equal(settings.isInformTrackedIgnored(), settingsPanel.isInformTrackedIgnored()) + || !Comparing.equal(settings.isNotifyIgnoredEditing(), settingsPanel.isNotifyIgnoredEditing()) || !settingsPanel.getLanguagesSettings().equalSettings(settings.getLanguagesSettings()); } @@ -126,6 +127,7 @@ public void apply() throws ConfigurationException { settings.setLanguagesSettings(settingsPanel.getLanguagesSettings().getSettings()); settings.setUnignoreActions(settingsPanel.isUnignoreActions()); settings.setInformTrackedIgnored(settingsPanel.isInformTrackedIgnored()); + settings.setNotifyIgnoredEditing(settingsPanel.isNotifyIgnoredEditing()); } /** Load settings from other components to configurable. */ @@ -142,6 +144,7 @@ public void reset() { settingsPanel.setAddUnversionedFiles(settings.isAddUnversionedFiles()); settingsPanel.setUnignoreActions(settings.isUnignoreActions()); settingsPanel.setInformTrackedIgnored(settings.isInformTrackedIgnored()); + settingsPanel.setNotifyIgnoredEditing(settings.isNotifyIgnoredEditing()); IgnoreSettingsPanel.LanguagesTableModel model = settingsPanel.getLanguagesSettings(); model.update(settings.getLanguagesSettings().clone()); diff --git a/src/mobi/hsz/idea/gitignore/ui/IgnoreSettingsPanel.form b/src/mobi/hsz/idea/gitignore/ui/IgnoreSettingsPanel.form index 6cd4c6de..6a5eee92 100644 --- a/src/mobi/hsz/idea/gitignore/ui/IgnoreSettingsPanel.form +++ b/src/mobi/hsz/idea/gitignore/ui/IgnoreSettingsPanel.form @@ -3,12 +3,12 @@ - + - + @@ -16,9 +16,6 @@ - - - @@ -77,6 +74,14 @@ + + + + + + + + @@ -85,9 +90,6 @@ - - - @@ -104,9 +106,6 @@ - - - diff --git a/src/mobi/hsz/idea/gitignore/ui/IgnoreSettingsPanel.java b/src/mobi/hsz/idea/gitignore/ui/IgnoreSettingsPanel.java index 542fec09..d670483d 100644 --- a/src/mobi/hsz/idea/gitignore/ui/IgnoreSettingsPanel.java +++ b/src/mobi/hsz/idea/gitignore/ui/IgnoreSettingsPanel.java @@ -130,6 +130,9 @@ public class IgnoreSettingsPanel implements Disposable { /** Panel with information about donations. */ private JPanel donatePanel; + /** Inform about editing ignored file. */ + private JCheckBox notifyIgnoredEditing; + /** Editor panel element. */ private EditorPanel editorPanel; @@ -342,6 +345,24 @@ public void setInformTrackedIgnored(boolean selected) { this.informTrackedIgnored.setSelected(selected); } + /** + * Returns value of @{link {@link #notifyIgnoredEditing}} field. + * + * @return {@link #notifyIgnoredEditing} is selected + */ + public boolean isNotifyIgnoredEditing() { + return notifyIgnoredEditing.isSelected(); + } + + /** + * Sets value of {@link #notifyIgnoredEditing} field. + * + * @param selected value for {@link #notifyIgnoredEditing} + */ + public void setNotifyIgnoredEditing(boolean selected) { + this.notifyIgnoredEditing.setSelected(selected); + } + /** * Returns model of {@link #languagesTable}. * diff --git a/src/mobi/hsz/idea/gitignore/util/Properties.java b/src/mobi/hsz/idea/gitignore/util/Properties.java index 9c1849f4..1b87fb3d 100644 --- a/src/mobi/hsz/idea/gitignore/util/Properties.java +++ b/src/mobi/hsz/idea/gitignore/util/Properties.java @@ -26,9 +26,13 @@ import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import java.util.HashSet; + /** * {@link Properties} util class that holds project specified settings using {@link PropertiesComponent}. * @@ -38,51 +42,96 @@ public class Properties { /** Ignore missing gitignore property key. */ @NonNls - private static final String PROP_IGNORE_MISSING_GITIGNORE = "ignore_missing_gitignore"; + private static final String IGNORE_MISSING_GITIGNORE = "ignore_missing_gitignore"; /** Add unversioned files property key. */ @NonNls - private static final String PROP_ADD_UNVERSIONED_FILES = "prop_add_unversioned_files"; + private static final String ADD_UNVERSIONED_FILES = "add_unversioned_files"; + + /** Dismissed ignored editing notification key. */ + @NonNls + private static final String DISMISSED_IGNORED_EDITING_NOTIFICATION = "add_unversioned_files"; /** Private constructor to prevent creating {@link Properties} instance. */ private Properties() { } /** - * Checks value of {@link #PROP_IGNORE_MISSING_GITIGNORE} key in {@link PropertiesComponent}. + * Checks value of {@link #IGNORE_MISSING_GITIGNORE} key in {@link PropertiesComponent}. * * @param project current project - * @return {@link #PROP_IGNORE_MISSING_GITIGNORE} value + * @return {@link #IGNORE_MISSING_GITIGNORE} value */ public static boolean isIgnoreMissingGitignore(@NotNull Project project) { - return PropertiesComponent.getInstance(project).getBoolean(PROP_IGNORE_MISSING_GITIGNORE, false); + return properties(project).getBoolean(IGNORE_MISSING_GITIGNORE, false); } /** - * Sets value of {@link #PROP_IGNORE_MISSING_GITIGNORE} key in {@link PropertiesComponent} to true. + * Sets value of {@link #IGNORE_MISSING_GITIGNORE} key in {@link PropertiesComponent} to true. * * @param project current project */ public static void setIgnoreMissingGitignore(@NotNull Project project) { - PropertiesComponent.getInstance(project).setValue(PROP_IGNORE_MISSING_GITIGNORE, Boolean.TRUE.toString()); + properties(project).setValue(IGNORE_MISSING_GITIGNORE, Boolean.TRUE.toString()); } /** - * Checks value of {@link #PROP_ADD_UNVERSIONED_FILES} key in {@link PropertiesComponent}. + * Checks value of {@link #ADD_UNVERSIONED_FILES} key in {@link PropertiesComponent}. * * @param project current project - * @return {@link #PROP_ADD_UNVERSIONED_FILES} value + * @return {@link #ADD_UNVERSIONED_FILES} value */ public static boolean isAddUnversionedFiles(@NotNull Project project) { - return PropertiesComponent.getInstance(project).getBoolean(PROP_ADD_UNVERSIONED_FILES, false); + return properties(project).getBoolean(ADD_UNVERSIONED_FILES, false); } /** - * Sets value of {@link #PROP_ADD_UNVERSIONED_FILES} key in {@link PropertiesComponent} to true. + * Sets value of {@link #ADD_UNVERSIONED_FILES} key in {@link PropertiesComponent} to true. * * @param project current project */ public static void setAddUnversionedFiles(@NotNull Project project) { - PropertiesComponent.getInstance(project).setValue(PROP_ADD_UNVERSIONED_FILES, Boolean.TRUE.toString()); + properties(project).setValue(ADD_UNVERSIONED_FILES, Boolean.TRUE.toString()); + } + + /** + * Checks if user already dismissed notification about editing ignored file. + * + * @param project current project + * @param file current file + * @return notification was dismissed + */ + public static boolean isDismissedIgnoredEditingNotification(@NotNull Project project, @NotNull VirtualFile file) { + final PropertiesComponent props = properties(project); + String[] values = props.getValues(DISMISSED_IGNORED_EDITING_NOTIFICATION); + + return ContainerUtil.newHashSet(values != null ? values : new String[0]).contains(file.getCanonicalPath()); + } + + /** + * Stores information about dismissed notification about editing ignored file. + * + * @param project current project + * @param file current file + */ + public static void setDismissedIgnoredEditingNotification(@NotNull Project project, @NotNull VirtualFile file) { + final PropertiesComponent props = properties(project); + String[] values = props.getValues(DISMISSED_IGNORED_EDITING_NOTIFICATION); + + final HashSet set = ContainerUtil.newHashSet(values != null ? values : new String[0]); + set.add(file.getCanonicalPath()); + + props.setValues(DISMISSED_IGNORED_EDITING_NOTIFICATION, set.toArray(new String[set.size()])); + } + + /** + * Shorthand for {@link PropertiesComponent#getInstance} method. + * + * @param project current project + * @return component instance + */ + @NotNull + private static PropertiesComponent properties(@NotNull Project project) { + return PropertiesComponent.getInstance(project); } }