/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.psi.codeStyle.autodetect; import com.intellij.ide.actions.ShowSettingsUtilImpl; import com.intellij.lang.LanguageFormatting; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.application.ApplicationManager; 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; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiCompiledFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.*; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.WeakList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import java.util.List; import static com.intellij.psi.codeStyle.EditorNotificationInfo.ActionLabelData; /** * @author Rustam Vishnyakov */ public class DetectableIndentOptionsProvider extends FileIndentOptionsProvider implements ProviderForCommittedDocument { private boolean myIsEnabledInTest; private final List<VirtualFile> myAcceptedFiles = new WeakList<VirtualFile>(); private final List<VirtualFile> myDisabledFiles = new WeakList<VirtualFile>(); @Nullable @Override public CommonCodeStyleSettings.IndentOptions getIndentOptions(@NotNull CodeStyleSettings settings, @NotNull PsiFile file) { return isDocumentCommitted(file) && isEnabled(settings, file) ? new IndentOptionsDetectorImpl(file).getIndentOptions() : null; } private static boolean isDocumentCommitted(@NotNull PsiFile file) { PsiDocumentManager manager = PsiDocumentManager.getInstance(file.getProject()); Document document = manager.getDocument(file); return document != null && manager.isCommitted(document); } @Override public boolean useOnFullReformat() { return false; } @TestOnly public void setEnabledInTest(boolean isEnabledInTest) { myIsEnabledInTest = isEnabledInTest; } private boolean isEnabled(@NotNull CodeStyleSettings settings, @NotNull PsiFile file) { if (file instanceof PsiCompiledFile) return false; if (ApplicationManager.getApplication().isUnitTestMode()) { return myIsEnabledInTest; } VirtualFile vFile = file.getVirtualFile(); if (vFile == null || vFile instanceof LightVirtualFile || myDisabledFiles.contains(vFile)) return false; return LanguageFormatting.INSTANCE.forContext(file) != null && settings.AUTODETECT_INDENTS; } @TestOnly @Nullable public static DetectableIndentOptionsProvider getInstance() { return FileIndentOptionsProvider.EP_NAME.findExtension(DetectableIndentOptionsProvider.class); } @Nullable @Override public EditorNotificationInfo getNotificationInfo(@NotNull final Project project, @NotNull final VirtualFile file, @NotNull final FileEditor fileEditor, @NotNull CommonCodeStyleSettings.IndentOptions userOptions, @NotNull CommonCodeStyleSettings.IndentOptions detectedOptions) { final NotificationLabels labels = getNotificationLabels(userOptions, detectedOptions); final Editor editor = fileEditor instanceof TextEditor ? ((TextEditor)fileEditor).getEditor() : null; if (labels == null || editor == null) return null; ActionLabelData okAction = new ActionLabelData( ApplicationBundle.message("code.style.indents.detector.accept"), new Runnable() { @Override public void run() { setAccepted(file); } } ); ActionLabelData disableForSingleFile = new ActionLabelData( labels.revertToOldSettingsLabel, new Runnable() { @Override public void run() { disableForFile(file); if (editor instanceof EditorEx) { ((EditorEx)editor).reinitSettings(); } } } ); ActionLabelData showSettings = new ActionLabelData( ApplicationBundle.message("code.style.indents.detector.show.settings"), new Runnable() { @Override public void run() { ShowSettingsUtilImpl.showSettingsDialog(project, "preferences.sourceCode", "detect indent"); } } ); final List<ActionLabelData> actions = ContainerUtil.newArrayList(okAction, disableForSingleFile, showSettings); return new EditorNotificationInfo() { @NotNull @Override public List<ActionLabelData> getLabelAndActions() { return actions; } @NotNull @Override public String getTitle() { return labels.title; } }; } @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; } private void disableForFile(@NotNull VirtualFile file) { myDisabledFiles.add(file); } @Override public void setAccepted(@NotNull VirtualFile file) { myAcceptedFiles.add(file); } @Override public boolean isAcceptedWithoutWarning(@Nullable Project project, @NotNull VirtualFile file) { return !FileIndentOptionsProvider.isShowNotification() || 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; } } }