Detected indent notification: renamed labels, FileIndentOptionsProvider now provides information needed to create user notification, added link to settings (IDEA-135929, IDEA-136063, IDEA-135942)

This commit is contained in:
Yaroslav Lepenkin
2015-02-09 18:12:08 +03:00
parent 773782545e
commit a7d52cffde
6 changed files with 219 additions and 107 deletions
@@ -0,0 +1,69 @@
/*
* 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.psi.codeStyle;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Collections;
import java.util.List;
public class EditorNotificationInfo {
private String myTitle;
private Icon myIcon;
private List<LabelWithAction> myLabelsWithActions = ContainerUtil.newArrayList();
public EditorNotificationInfo(@NotNull String title,
@NotNull LabelWithAction firstLabel,
@Nullable LabelWithAction... otherLabels)
{
myTitle = title;
myLabelsWithActions.add(firstLabel);
if (otherLabels != null) {
Collections.addAll(myLabelsWithActions, otherLabels);
}
}
public EditorNotificationInfo(@NotNull String title,
@NotNull Icon icon,
@NotNull LabelWithAction firstLabel,
@Nullable LabelWithAction... otherLabels)
{
this(title, firstLabel, otherLabels);
myIcon = icon;
}
@NotNull
public List<LabelWithAction> getLabelAndActions() {
return myLabelsWithActions;
}
@Nullable
public Icon getIcon() {
return myIcon;
}
@NotNull
public String getTitle() {
return myTitle;
}
}
@@ -16,9 +16,11 @@
package com.intellij.psi.codeStyle;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.ui.EditorNotificationPanel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -27,7 +29,7 @@ import javax.swing.*;
/**
* @author Rustam Vishnyakov
*/
public abstract class FileIndentOptionsProvider {
public abstract class FileIndentOptionsProvider {
public final static ExtensionPointName<FileIndentOptionsProvider> EP_NAME = ExtensionPointName.create("com.intellij.fileIndentOptionsProvider");
/**
@@ -48,36 +50,18 @@ public abstract class FileIndentOptionsProvider {
}
/**
* @return A name which will be used to notify a user on overwritten indent options. If the option is <code>null</code>, no notification
* @return information used to create user notification in editor. If the option is <code>null</code>, no notification
* will be shown.
*/
@Nullable
public String getDisplayName() {
public EditorNotificationInfo getNotificationInfo(@NotNull Project project,
@NotNull VirtualFile file,
@NotNull FileEditor fileEditor,
@NotNull CommonCodeStyleSettings.IndentOptions user,
@NotNull CommonCodeStyleSettings.IndentOptions detected) {
return null;
}
/**
* @return The icon to be displayed in a notification message, can be <coe>null</coe> (no icon).
*/
@Nullable
public Icon getIcon() {
return null;
}
/**
* @return <code>True</code> if the provider can be disabled (default is <code>false</code>).
*/
public boolean canBeDisabled() {
return false;
}
/**
* Disables the provider.
* @param project The project to disable the provider for.
*/
public void disable(@NotNull Project project) {
}
/**
* Tells if there should not be any notification for this specific file.
* @param file The file to check.
@@ -0,0 +1,29 @@
/*
* 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.psi.codeStyle;
import org.jetbrains.annotations.NotNull;
public class LabelWithAction {
public final String label;
public final Runnable action;
public LabelWithAction(@NotNull String label, @NotNull Runnable action) {
this.label = label;
this.action = action;
}
}
@@ -15,16 +15,19 @@
*/
package com.intellij.psi.codeStyle.autodetect;
import com.intellij.ide.actions.ShowSettingsUtilImpl;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.FileIndentOptionsProvider;
import com.intellij.psi.codeStyle.*;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.ui.EditorNotifications;
import com.intellij.util.containers.WeakList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,6 +41,7 @@ import java.util.List;
public class DetectableIndentOptionsProvider extends FileIndentOptionsProvider {
private boolean myIsEnabledInTest;
private final List<VirtualFile> myAcceptedFiles = new WeakList<VirtualFile>();
private final List<VirtualFile> myDisabledFiles = new WeakList<VirtualFile>();
@Nullable
@Override
@@ -60,7 +64,7 @@ public class DetectableIndentOptionsProvider extends FileIndentOptionsProvider {
return myIsEnabledInTest;
}
VirtualFile vFile = file.getVirtualFile();
if (vFile == null || vFile instanceof LightVirtualFile) return false;
if (vFile == null || vFile instanceof LightVirtualFile || myDisabledFiles.contains(vFile)) return false;
return settings.AUTODETECT_INDENTS;
}
@@ -72,21 +76,83 @@ public class DetectableIndentOptionsProvider extends FileIndentOptionsProvider {
@Nullable
@Override
public String getDisplayName() {
return ApplicationBundle.message("code.style.indents.detector.display.name");
public EditorNotificationInfo getNotificationInfo(@NotNull final Project project,
@NotNull final VirtualFile file,
@NotNull final FileEditor fileEditor,
@NotNull CommonCodeStyleSettings.IndentOptions userOptions,
@NotNull CommonCodeStyleSettings.IndentOptions detectedOptions)
{
NotificationLabels labels = getNotificationLabels(userOptions, detectedOptions);
final Editor editor = fileEditor instanceof TextEditor ? ((TextEditor)fileEditor).getEditor() : null;
if (labels == null || editor == null) return null;
LabelWithAction okAction = new LabelWithAction(
ApplicationBundle.message("code.style.indents.detector.accept"),
new Runnable() {
@Override
public void run() {
setAccepted(file);
EditorNotifications.getInstance(project).updateAllNotifications();
}
}
);
LabelWithAction disableForSingleFile = new LabelWithAction(
labels.revertToOldSettingsLabel,
new Runnable() {
@Override
public void run() {
disableForFile(file);
if (editor instanceof EditorEx) {
((EditorEx)editor).reinitSettings();
}
EditorNotifications.getInstance(project).updateAllNotifications();
}
}
);
LabelWithAction showSettings = new LabelWithAction(
ApplicationBundle.message("code.style.indents.detector.show.settings"),
new Runnable() {
@Override
public void run() {
ShowSettingsUtilImpl.showSettingsDialog(project, "preferences.sourceCode",
ApplicationBundle.message("settings.code.style.general.autodetect.indents"));
EditorNotifications.getInstance(project).updateAllNotifications();
}
}
);
return new EditorNotificationInfo(labels.title, okAction, disableForSingleFile, showSettings);
}
@Override
public boolean canBeDisabled() {
return true;
@Nullable
private static NotificationLabels getNotificationLabels(@NotNull CommonCodeStyleSettings.IndentOptions userOptions,
@NotNull CommonCodeStyleSettings.IndentOptions detectedOptions) {
if (userOptions.USE_TAB_CHARACTER) {
if (!detectedOptions.USE_TAB_CHARACTER) {
return new NotificationLabels(ApplicationBundle.message("code.style.space.indent.detected", detectedOptions.INDENT_SIZE),
ApplicationBundle.message("code.style.detector.use.tabs"));
}
}
else {
String restoreToSpaces = ApplicationBundle.message("code.style.detector.use.spaces", userOptions.INDENT_SIZE);
if (detectedOptions.USE_TAB_CHARACTER) {
return new NotificationLabels(ApplicationBundle.message("code.style.tab.usage.detected", userOptions.INDENT_SIZE),
restoreToSpaces);
}
if (userOptions.INDENT_SIZE != detectedOptions.INDENT_SIZE) {
return new NotificationLabels(ApplicationBundle.message("code.style.different.indent.size.detected", detectedOptions.INDENT_SIZE, userOptions.INDENT_SIZE),
restoreToSpaces);
}
}
return null;
}
@Override
public void disable(@NotNull Project project) {
CodeStyleSettingsManager.getSettings(project).AUTODETECT_INDENTS = false;
myAcceptedFiles.clear();
private void disableForFile(@NotNull VirtualFile file) {
myDisabledFiles.add(file);
}
@Override
public void setAccepted(@NotNull VirtualFile file) {
myAcceptedFiles.add(file);
@@ -96,4 +162,14 @@ public class DetectableIndentOptionsProvider extends FileIndentOptionsProvider {
public boolean isAcceptedWithoutWarning(@NotNull VirtualFile file) {
return myAcceptedFiles.contains(file);
}
private static class NotificationLabels {
public final String title;
public final String revertToOldSettingsLabel;
public NotificationLabels(@NotNull String title, @NotNull String revertToOldSettingsLabel) {
this.title = title;
this.revertToOldSettingsLabel = revertToOldSettingsLabel;
}
}
}
@@ -15,10 +15,8 @@
*/
package com.intellij.psi.codeStyle.autodetect;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.project.Project;
@@ -27,10 +25,7 @@ import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.FileIndentOptionsProvider;
import com.intellij.psi.codeStyle.*;
import com.intellij.ui.EditorNotificationPanel;
import com.intellij.ui.EditorNotifications;
import com.intellij.util.Processor;
@@ -73,41 +68,17 @@ public class DetectedIndentOptionsNotificationProvider extends EditorNotificatio
}
});
final FileIndentOptionsProvider provider = indentOptionsProviderRef.get();
if (provider != null &&
!provider.isAcceptedWithoutWarning(file) &&
provider.getDisplayName() != null &&
!userOptions.equals(detectedOptions)) {
final EditorNotificationPanel panel =
new EditorNotificationPanel()
.text(ApplicationBundle.message("code.style.indents.detector.message", provider.getDisplayName(),
getOptionDiffInfoString(userOptions, detectedOptions)));
if (provider.getIcon() != null) {
panel.icon(provider.getIcon());
EditorNotificationInfo info = provider != null && !provider.isAcceptedWithoutWarning(file) && !userOptions.equals(detectedOptions)
? provider.getNotificationInfo(project, file, fileEditor, userOptions, detectedOptions)
: null;
if (info != null) {
EditorNotificationPanel panel = new EditorNotificationPanel().text(info.getTitle());
if (info.getIcon() != null) {
panel.icon(info.getIcon());
}
panel.createActionLabel(
ApplicationBundle.message("code.style.indents.detector.accept"),
new Runnable() {
@Override
public void run() {
provider.setAccepted(file);
EditorNotifications.getInstance(project).updateAllNotifications();
}
}
);
if (provider.canBeDisabled()) {
panel.createActionLabel(
ApplicationBundle.message("code.style.indents.detector.disable"),
new Runnable() {
@Override
public void run() {
provider.disable(project);
if (editor instanceof EditorEx) {
((EditorEx)editor).reinitSettings();
}
EditorNotifications.getInstance(project).updateAllNotifications();
}
}
);
for (LabelWithAction action : info.getLabelAndActions()) {
panel.createActionLabel(action.label, action.action);
}
return panel;
}
@@ -116,26 +87,4 @@ public class DetectedIndentOptionsNotificationProvider extends EditorNotificatio
}
return null;
}
@NotNull
private static String getOptionDiffInfoString(CommonCodeStyleSettings.IndentOptions user,
CommonCodeStyleSettings.IndentOptions detected) {
StringBuilder sb = new StringBuilder();
if (user.INDENT_SIZE != detected.INDENT_SIZE) {
sb.append("indent size=").append(detected.INDENT_SIZE);
}
if (user.TAB_SIZE != detected.TAB_SIZE) {
if (sb.length() > 0) sb.append(", ");
sb.append("tab size=").append(detected.TAB_SIZE);
}
if (user.USE_TAB_CHARACTER != detected.USE_TAB_CHARACTER) {
if (sb.length() > 0) sb.append(", ");
sb.append(detected.USE_TAB_CHARACTER ? "tabs" : "no tabs");
}
if (user.SMART_TABS != detected.SMART_TABS) {
if (sb.length() > 0) sb.append(", ");
sb.append(detected.SMART_TABS ? "smart tabs" : "no smart tabs");
}
return sb.toString();
}
}
@@ -685,7 +685,12 @@ reformat.changed.text.file.too.big.notification.text=file {0} is too big or ther
bulk.reformat.prepare.progress.text=Preparing...
bulk.reformat.process.progress.text=Processing...
code.style.indents.detector.display.name=Automatic indent options detector
code.style.indents.detector.message=Indent options for the file are overwritten by {0} ({1})
code.style.indents.detector.accept=OK
code.style.indents.detector.disable=Disable for all files
code.style.different.indent.size.detected=This file is indented with {0} spaces instead of {1}
code.style.tab.usage.detected=This file is indented with tabs instead of {0} spaces
code.style.space.indent.detected=This file is indented with {0} spaces instead of the tab character
code.style.detector.use.spaces=Indent with {0} spaces
code.style.detector.use.tabs=Indent with tab
code.style.indents.detector.show.settings=Show Settings
code.style.indents.detector.accept=OK