| /* |
| * Copyright 2010 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.application.options.codeStyle; |
| |
| import com.intellij.application.options.CodeStyleAbstractPanel; |
| import com.intellij.lang.Language; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.colors.EditorColorsScheme; |
| import com.intellij.openapi.editor.highlighter.EditorHighlighter; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.fileTypes.FileType; |
| import com.intellij.openapi.fileTypes.FileTypeEditorHighlighterProviders; |
| import com.intellij.openapi.fileTypes.StdFileTypes; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ProjectUtil; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.codeStyle.CodeStyleManager; |
| import com.intellij.psi.codeStyle.CodeStyleSettings; |
| import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable; |
| import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; |
| import com.intellij.ui.AncestorListenerAdapter; |
| import com.intellij.ui.components.JBTabbedPane; |
| import com.intellij.util.Function; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.MultiMap; |
| import gnu.trove.THashSet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.AncestorEvent; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import java.awt.*; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * Base class for code style settings panels supporting multiple programming languages. |
| * |
| * @author rvishnyakov |
| */ |
| public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstractPanel implements CodeStyleSettingsCustomizable { |
| private static final Logger LOG = Logger.getInstance("com.intellij.application.options.codeStyle.MultilanguageCodeStyleAbstractPanel"); |
| |
| private Language myLanguage; |
| private int myLangSelectionIndex; |
| private JTabbedPane tabbedPane; |
| |
| protected MultilanguageCodeStyleAbstractPanel(CodeStyleSettings settings) { |
| super(settings); |
| } |
| |
| protected void init() { |
| customizeSettings(); |
| } |
| |
| protected void customizeSettings() { |
| for (LanguageCodeStyleSettingsProvider provider : Extensions.getExtensions(LanguageCodeStyleSettingsProvider.EP_NAME)) { |
| if (provider.usesSharedPreview()) { |
| resetDefaultNames(); |
| provider.customizeSettings(this, getSettingsType()); |
| } |
| } |
| } |
| |
| @Override |
| public boolean setPanelLanguage(Language language) { |
| |
| boolean languageProviderFound = false; |
| for (LanguageCodeStyleSettingsProvider provider : Extensions.getExtensions(LanguageCodeStyleSettingsProvider.EP_NAME)) { |
| if (provider.getLanguage().is(language)) { |
| resetDefaultNames(); |
| provider.customizeSettings(this, getSettingsType()); |
| languageProviderFound = true; |
| break; |
| } |
| } |
| if (!languageProviderFound) return false; |
| |
| myLanguage = language; |
| |
| setSkipPreviewHighlighting(true); |
| try { |
| onLanguageChange(language); |
| } |
| finally { |
| setSkipPreviewHighlighting(false); |
| } |
| updatePreview(true); |
| return true; |
| } |
| |
| public Language getSelectedLanguage() { |
| return myLanguage; |
| } |
| |
| public abstract LanguageCodeStyleSettingsProvider.SettingsType getSettingsType(); |
| |
| protected void onLanguageChange(Language language) { |
| } |
| |
| protected void resetDefaultNames() { |
| } |
| |
| @Override |
| protected String getPreviewText() { |
| if (myLanguage == null) return ""; |
| String sample = LanguageCodeStyleSettingsProvider.getCodeSample(myLanguage, getSettingsType()); |
| if (sample == null) return ""; |
| return sample; |
| } |
| |
| @Override |
| protected PsiFile createFileFromText(final Project project, final String text) { |
| final PsiFile file = LanguageCodeStyleSettingsProvider.createFileFromText(myLanguage, project, text); |
| return file != null ? file : super.createFileFromText(project, text); |
| } |
| |
| @Override |
| protected int getRightMargin() { |
| if (myLanguage == null) return -1; |
| return LanguageCodeStyleSettingsProvider.getRightMargin(myLanguage, getSettingsType()); |
| } |
| |
| @Override |
| protected String getFileExt() { |
| String fileExt = LanguageCodeStyleSettingsProvider.getFileExt(myLanguage); |
| if (fileExt != null) return fileExt; |
| return super.getFileExt(); |
| } |
| |
| @NotNull |
| @Override |
| protected FileType getFileType() { |
| if (myLanguage != null) { |
| FileType assocType = myLanguage.getAssociatedFileType(); |
| if (assocType != null) { |
| return assocType; |
| } |
| } |
| Language[] langs = LanguageCodeStyleSettingsProvider.getLanguagesWithCodeStyleSettings(); |
| if (langs.length > 0) { |
| myLanguage = langs[0]; |
| FileType type = langs[0].getAssociatedFileType(); |
| if (type != null) return type; |
| } |
| return StdFileTypes.JAVA; |
| } |
| |
| @Override |
| @Nullable |
| protected EditorHighlighter createHighlighter(final EditorColorsScheme scheme) { |
| FileType fileType = getFileType(); |
| return FileTypeEditorHighlighterProviders.INSTANCE.forFileType(fileType).getEditorHighlighter( |
| ProjectUtil.guessCurrentProject(getPanel()), fileType, null, scheme); |
| } |
| |
| |
| @Override |
| protected PsiFile doReformat(final Project project, final PsiFile psiFile) { |
| final String text = psiFile.getText(); |
| final PsiDocumentManager manager = PsiDocumentManager.getInstance(project); |
| final Document doc = manager.getDocument(psiFile); |
| CommandProcessor.getInstance().executeCommand(project, new Runnable() { |
| @Override |
| public void run() { |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| doc.replaceString(0, doc.getTextLength(), text); |
| manager.commitDocument(doc); |
| try { |
| CodeStyleManager.getInstance(project).reformat(psiFile); |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| }); |
| } |
| }, "", ""); |
| if (doc != null) { |
| manager.commitDocument(doc); |
| } |
| return psiFile; |
| } |
| |
| protected static JPanel createPreviewPanel() { |
| return new JPanel(new BorderLayout()); |
| } |
| |
| |
| @Override |
| protected void installPreviewPanel(final JPanel previewPanel) { |
| if (getSettingsType() != LanguageCodeStyleSettingsProvider.SettingsType.LANGUAGE_SPECIFIC) { |
| tabbedPane = new JBTabbedPane(); |
| tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); |
| Language[] langs = LanguageCodeStyleSettingsProvider.getLanguagesWithSharedPreview(); |
| if (langs.length == 0) return; |
| for (Language lang : langs) { |
| tabbedPane.addTab(getTabName(lang), createDummy()); |
| } |
| tabbedPane.setComponentAt(0, getEditor().getComponent()); |
| myLangSelectionIndex = 0; |
| if (myLanguage == null) { |
| setPanelLanguage(langs[0]); |
| } |
| else { |
| updatePreview(true); |
| } |
| tabbedPane.addChangeListener(new ChangeListener() { |
| @Override |
| public void stateChanged(ChangeEvent e) { |
| onTabSelection((JTabbedPane)e.getSource()); |
| } |
| }); |
| previewPanel.add(tabbedPane, BorderLayout.CENTER); |
| previewPanel.addAncestorListener(new AncestorListenerAdapter() { |
| @Override |
| public void ancestorAdded(AncestorEvent event) { |
| selectCurrentLanguageTab(); |
| } |
| }); |
| } |
| else { |
| // If settings are language-specific |
| previewPanel.add(getEditor().getComponent(), BorderLayout.CENTER); |
| updatePreview(true); |
| } |
| } |
| |
| private void selectCurrentLanguageTab() { |
| for (int i = 0; i < tabbedPane.getTabCount(); i++) { |
| if (getTabName(myLanguage).equals(tabbedPane.getTitleAt(i))) { |
| tabbedPane.setSelectedIndex(i); |
| return; |
| } |
| } |
| } |
| |
| private void onTabSelection(JTabbedPane tabs) { |
| int i = tabs.getSelectedIndex(); |
| tabs.setComponentAt(myLangSelectionIndex, createDummy()); |
| tabs.setComponentAt(i, getEditor().getComponent()); |
| myLangSelectionIndex = i; |
| String selectionTitle = tabs.getTitleAt(i); |
| Language lang = LanguageCodeStyleSettingsProvider.getLanguage(selectionTitle); |
| if (lang != null && getLanguageSelector() != null) { |
| getLanguageSelector().setLanguage(lang); |
| } |
| } |
| |
| |
| private static JComponent createDummy() { |
| return new JLabel(""); |
| } |
| |
| private static String getTabName(Language language) { |
| String tabName = LanguageCodeStyleSettingsProvider.getLanguageName(language); |
| if (tabName == null) tabName = language.getDisplayName(); |
| return tabName; |
| } |
| |
| @Override |
| public Language getDefaultLanguage() { |
| return getSelectedLanguage(); |
| } |
| |
| @Override |
| public void moveStandardOption(String fieldName, String newGroup) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| protected <T extends OrderedOption>List<T> sortOptions(Collection<T> options) { |
| Set<String> names = new THashSet<String>(ContainerUtil.map(options, new Function<OrderedOption, String>() { |
| @Override |
| public String fun(OrderedOption option) { |
| return option.getOptionName(); |
| } |
| })); |
| |
| List<T> order = new ArrayList<T>(options.size()); |
| MultiMap<String, T> afters = new MultiMap<String, T>(); |
| MultiMap<String, T> befores = new MultiMap<String, T>(); |
| |
| for (T each : options) { |
| String anchorOptionName = each.getAnchorOptionName(); |
| if (anchorOptionName != null && names.contains(anchorOptionName)) { |
| if (each.getAnchor() == OptionAnchor.AFTER) { |
| afters.putValue(anchorOptionName, each); |
| continue; |
| } |
| else if (each.getAnchor() == OptionAnchor.BEFORE) { |
| befores.putValue(anchorOptionName, each); |
| continue; |
| } |
| } |
| order.add(each); |
| } |
| |
| List<T> result = new ArrayList<T>(options.size()); |
| for (T each : order) { |
| result.addAll(befores.get(each.getOptionName())); |
| result.add(each); |
| result.addAll(afters.get(each.getOptionName())); |
| } |
| |
| assert result.size() == options.size(); |
| return result; |
| } |
| |
| protected abstract static class OrderedOption { |
| @NotNull private final String optionName; |
| @Nullable private final OptionAnchor anchor; |
| @Nullable private final String anchorOptionName; |
| |
| protected OrderedOption(@NotNull String optionName, |
| OptionAnchor anchor, |
| String anchorOptionName) { |
| this.optionName = optionName; |
| this.anchor = anchor; |
| this.anchorOptionName = anchorOptionName; |
| } |
| |
| @NotNull |
| public String getOptionName() { |
| return optionName; |
| } |
| |
| @Nullable |
| public OptionAnchor getAnchor() { |
| return anchor; |
| } |
| |
| @Nullable |
| public String getAnchorOptionName() { |
| return anchorOptionName; |
| } |
| } |
| } |