Merge branch 'configure-code-style-on-selected-fragment'
authorYaroslav Lepenkin <yaroslav.lepenkin@jetbrains.com>
Fri, 13 Mar 2015 17:27:43 +0000 (20:27 +0300)
committerYaroslav Lepenkin <yaroslav.lepenkin@jetbrains.com>
Fri, 13 Mar 2015 17:27:43 +0000 (20:27 +0300)
16 files changed:
java/java-impl/src/com/intellij/application/options/JavaCodeStyleSettingsProvider.java
platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java
platform/lang-impl/src/com/intellij/application/options/TabbedLanguageCodeStylePanel.java
platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java
platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java
platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java
platform/lang-impl/src/com/intellij/formatting/contextConfiguration/CodeFragmentCodeStyleSettingsPanel.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/formatting/contextConfiguration/ConfigureCodeStyleOnSelectedFragment.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/formatting/contextConfiguration/SelectedTextFormatter.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/psi/codeStyle/CodeStyleSettingsCodeFragmentFilter.java [new file with mode: 0644]
platform/platform-api/src/com/intellij/ui/components/editors/JBComboBoxTableCellEditorComponent.java
platform/platform-resources-en/src/intentionDescriptions/ConfigureCodeStyleOnSelectedFragment/after.txt.template [new file with mode: 0644]
platform/platform-resources-en/src/intentionDescriptions/ConfigureCodeStyleOnSelectedFragment/before.txt.template [new file with mode: 0644]
platform/platform-resources-en/src/intentionDescriptions/ConfigureCodeStyleOnSelectedFragment/description.html [new file with mode: 0644]
platform/platform-resources-en/src/messages/CodeInsightBundle.properties
platform/platform-resources/src/META-INF/LangExtensions.xml

index 5f409f835b73d4ead4b0139d6322acaac3de0f41..8d12181d2a7047afe3b255fc04c983663efe8ec6 100644 (file)
@@ -15,6 +15,8 @@
  */
 package com.intellij.application.options;
 
+import com.intellij.lang.Language;
+import com.intellij.lang.java.JavaLanguage;
 import com.intellij.openapi.options.Configurable;
 import com.intellij.psi.codeStyle.*;
 import com.intellij.util.PlatformUtils;
@@ -48,6 +50,12 @@ public class JavaCodeStyleSettingsProvider extends CodeStyleSettingsProvider {
     return "Java";
   }
 
+  @Nullable
+  @Override
+  public Language getLanguage() {
+    return JavaLanguage.INSTANCE;
+  }
+
   @Nullable
   @Override
   public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) {
index 3e7fb8a0f94c11a3415b00a3b59decd7fb1b35d3..95f2f3bec0eba4607be85c89d7c88753c57d9f42 100644 (file)
@@ -17,16 +17,20 @@ package com.intellij.psi.codeStyle;
 
 import com.intellij.application.options.IndentOptionsEditor;
 import com.intellij.lang.Language;
+import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.extensions.ExtensionPointName;
 import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.PsiFile;
+import com.intellij.util.ArrayUtil;
 import com.intellij.util.containers.HashSet;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -202,6 +206,12 @@ public abstract class LanguageCodeStyleSettingsProvider {
     return fieldCollector.getCollectedFields();
   }
 
+  public Set<String> getSupportedFields(SettingsType type) {
+    SupportedFieldCollector fieldCollector = new SupportedFieldCollector();
+    fieldCollector.collectFields(type);
+    return fieldCollector.getCollectedFields();
+  }
+
   private final class SupportedFieldCollector implements CodeStyleSettingsCustomizable {
     private final Set<String> myCollectedFields = new HashSet<String>();
     private SettingsType myCurrSettingsType;
@@ -213,6 +223,11 @@ public abstract class LanguageCodeStyleSettingsProvider {
       }
     }
 
+    public void collectFields(SettingsType type) {
+      myCurrSettingsType = type;
+      LanguageCodeStyleSettingsProvider.this.customizeSettings(this, type);
+    }
+
     @Override
     public void showAllStandardOptions() {
       switch (myCurrSettingsType) {
@@ -275,5 +290,4 @@ public abstract class LanguageCodeStyleSettingsProvider {
       return myCollectedFields;
     }
   }
-
 }
index 4633ef9dcd2229a89f69e145e8b7ac09eeb24d4b..f6ffbb3aee27aa591b12ed63ca8f1e2656c65ea1 100644 (file)
@@ -119,7 +119,7 @@ public abstract class TabbedLanguageCodeStylePanel extends CodeStyleAbstractPane
     addTab(new MyWrappingAndBracesPanel(settings));
   }
 
-  private void ensureTabs() {
+  protected void ensureTabs() {
     if (myTabs == null) {
       myPanel = new JPanel();
       myPanel.setLayout(new BorderLayout());
index f8c059697f4ba0ca9e9e2be39bd1e7ed47cf75a1..959f12b7ec2e751002204a88021244e69ef97660 100644 (file)
@@ -53,7 +53,7 @@ import java.util.List;
  * @author max
  */
 public abstract class OptionTableWithPreviewPanel extends CustomizableLanguageCodeStylePanel {
-  private TreeTable myTreeTable;
+  protected TreeTable myTreeTable;
   private final JPanel myPanel = new JPanel();
 
   private final List<Option> myOptions = new ArrayList<Option>();
@@ -61,8 +61,7 @@ public abstract class OptionTableWithPreviewPanel extends CustomizableLanguageCo
   private final Set<String> myAllowedOptions = new THashSet<String>();
   private final Map<String, String> myRenamedFields = new THashMap<String, String>();
   private boolean myShowAllStandardOptions;
-  private boolean isFirstUpdate = true;
-
+  protected boolean isFirstUpdate = true;
 
   public OptionTableWithPreviewPanel(CodeStyleSettings settings) {
     super(settings);
index dba62efec75225e7f63bbf1de72019a76ef073b7..a03388a6565ef8da69f2f788b2ce5dce75159e01 100644 (file)
@@ -47,14 +47,14 @@ import java.util.List;
  */
 public abstract class OptionTreeWithPreviewPanel extends CustomizableLanguageCodeStylePanel {
   private static final Logger LOG = Logger.getInstance("#com.intellij.application.options.CodeStyleSpacesPanel");
-  private JTree myOptionsTree;
+  protected JTree myOptionsTree;
   private final ArrayList<BooleanOptionKey> myKeys = new ArrayList<BooleanOptionKey>();
   protected final JPanel myPanel = new JPanel(new GridBagLayout());
 
   private boolean myShowAllStandardOptions = false;
   private Set<String> myAllowedOptions = new HashSet<String>();
   private MultiMap<String, CustomBooleanOptionInfo> myCustomOptions = new MultiMap<String, CustomBooleanOptionInfo>();
-  private boolean isFirstUpdate = true;
+  protected boolean isFirstUpdate = true;
   private final Map<String, String> myRenamedFields = new THashMap<String, String>();
   private final Map<String, String> myRemappedGroups = new THashMap<String, String>();
 
@@ -422,7 +422,7 @@ public abstract class OptionTreeWithPreviewPanel extends CustomizableLanguageCod
     return renamed == null ? defaultTitle : renamed;
   }
 
-  private static class MyTreeCellRenderer implements TreeCellRenderer {
+  protected static class MyTreeCellRenderer implements TreeCellRenderer {
     private final JLabel myLabel;
     private final JCheckBox myCheckBox;
 
index bd3aa035b22a0fabb23216a596805fee05107fab..d25c6f964e2a78edc3eda118ca210df6f5bc0726 100644 (file)
@@ -18,8 +18,19 @@ package com.intellij.application.options.codeStyle;
 import com.intellij.openapi.application.ApplicationBundle;
 import com.intellij.psi.codeStyle.CodeStyleSettings;
 import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
 
 public class WrappingAndBracesPanel extends OptionTableWithPreviewPanel {
+  private MultiMap<String, String> myGroupToFields = new MultiMap<String, String>();
+  private Map<String, SettingsGroup> myFieldNameToGroup;
+
   public WrappingAndBracesPanel(CodeStyleSettings settings) {
     super(settings);
     init();
@@ -30,6 +41,23 @@ public class WrappingAndBracesPanel extends OptionTableWithPreviewPanel {
     return LanguageCodeStyleSettingsProvider.SettingsType.WRAPPING_AND_BRACES_SETTINGS;
   }
 
+  @Override
+  protected void addOption(@NotNull String fieldName, @NotNull String title, @Nullable String groupName) {
+    super.addOption(fieldName, title, groupName);
+    if (groupName != null) {
+      myGroupToFields.putValue(groupName, fieldName);
+    }
+  }
+
+  @Override
+  protected void addOption(@NotNull String fieldName, @NotNull String title, @Nullable String groupName,
+                           @NotNull String[] options, @NotNull int[] values) {
+    super.addOption(fieldName, title, groupName, options, values);
+    if (groupName == null) {
+      myGroupToFields.putValue(title, fieldName);
+    }
+  }
+
   @Override
   protected void initTables() {
     addOption("RIGHT_MARGIN", ApplicationBundle.message("editbox.right.margin.columns"), null, 0, 999, -1, ApplicationBundle.message("settings.code.style.default.general"));
@@ -136,8 +164,34 @@ public class WrappingAndBracesPanel extends OptionTableWithPreviewPanel {
     addOption("VARIABLE_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.local.variables.annotation"), WRAP_OPTIONS, WRAP_VALUES);
   }
 
+  protected SettingsGroup getAssociatedSettingsGroup(String fieldName) {
+    if (myFieldNameToGroup == null) {
+      myFieldNameToGroup = ContainerUtil.newHashMap();
+      Set<String> groups = myGroupToFields.keySet();
+      for (String group : groups) {
+        Collection<String> fields = myGroupToFields.get(group);
+        SettingsGroup settingsGroup = new SettingsGroup(group, fields);
+        for (String field : fields) {
+          myFieldNameToGroup.put(field, settingsGroup);
+        }
+      }
+    }
+    return myFieldNameToGroup.get(fieldName);
+  }
+
   @Override
   protected String getTabTitle() {
     return ApplicationBundle.message("wrapping.and.braces");
   }
+
+  protected static class SettingsGroup {
+    public final String title;
+    public final Collection<String> commonCodeStyleSettingFieldNames;
+
+    public SettingsGroup(@NotNull String title,
+                         @NotNull Collection<String> commonCodeStyleSettingFieldNames) {
+      this.title = title;
+      this.commonCodeStyleSettingFieldNames = commonCodeStyleSettingFieldNames;
+    }
+  }
 }
\ No newline at end of file
diff --git a/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/CodeFragmentCodeStyleSettingsPanel.java b/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/CodeFragmentCodeStyleSettingsPanel.java
new file mode 100644 (file)
index 0000000..a0a9b77
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * 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.formatting.contextConfiguration;
+
+import com.intellij.application.options.TabbedLanguageCodeStylePanel;
+import com.intellij.lang.Language;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsCodeFragmentFilter;
+import com.intellij.ui.components.JBScrollPane;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import static com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider.SettingsType.SPACING_SETTINGS;
+import static com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider.SettingsType.WRAPPING_AND_BRACES_SETTINGS;
+
+class CodeFragmentCodeStyleSettingsPanel extends TabbedLanguageCodeStylePanel {
+  private static final Logger LOG = Logger.getInstance(CodeFragmentCodeStyleSettingsPanel.class);
+
+  private final CodeStyleSettingsCodeFragmentFilter.CodeStyleSettingsToShow mySettingsToShow;
+  private final SelectedTextFormatter mySelectedTextFormatter;
+
+  public CodeFragmentCodeStyleSettingsPanel(@NotNull CodeStyleSettings settings,
+                                            @NotNull CodeStyleSettingsCodeFragmentFilter.CodeStyleSettingsToShow settingsToShow,
+                                            @NotNull Language language,
+                                            @NotNull SelectedTextFormatter selectedTextFormatter)
+  {
+    super(language, settings, settings.clone());
+    mySettingsToShow = settingsToShow;
+    mySelectedTextFormatter = selectedTextFormatter;
+
+    ensureTabs();
+  }
+
+  @Override
+  protected String getPreviewText() {
+    return null;
+  }
+
+  @Override
+  protected void updatePreview(boolean useDefaultSample) {
+  }
+
+  @Override
+  protected void initTabs(CodeStyleSettings settings) {
+    addTab(new SpacesPanelWithoutPreview(settings));
+    addTab(new WrappingAndBracesPanelWithoutPreview(settings));
+    reset(getSettings());
+  }
+
+  public static CodeStyleSettingsCodeFragmentFilter.CodeStyleSettingsToShow calcSettingNamesToShow(CodeStyleSettingsCodeFragmentFilter filter) {
+    return filter.getFieldNamesAffectingCodeFragment(SPACING_SETTINGS, WRAPPING_AND_BRACES_SETTINGS);
+  }
+
+  private void reformatSelectedTextWithNewSettings() {
+    try {
+      apply(getSettings());
+    }
+    catch (ConfigurationException e) {
+      LOG.debug("Cannot apply code style settings", e);
+    }
+
+    CodeStyleSettings clonedSettings = getSettings().clone();
+    mySelectedTextFormatter.reformatSelectedText(clonedSettings);
+  }
+
+  private class SpacesPanelWithoutPreview extends MySpacesPanel {
+    private JPanel myPanel;
+
+    public SpacesPanelWithoutPreview(CodeStyleSettings settings) {
+      super(settings);
+    }
+
+    @Override
+    protected void somethingChanged() {
+      mySelectedTextFormatter.restoreSelectedText();
+      reformatSelectedTextWithNewSettings();
+    }
+
+    @Override
+    protected void init() {
+      List<String> settingNames = mySettingsToShow.getSettings(getSettingsType());
+      String[] names = ContainerUtil.toArray(settingNames, new String[settingNames.size()]);
+      showStandardOptions(names);
+      initTables();
+
+      myOptionsTree = createOptionsTree();
+      myOptionsTree.setCellRenderer(new MyTreeCellRenderer());
+
+      JBScrollPane pane = new JBScrollPane(myOptionsTree) {
+        @Override
+        public Dimension getMinimumSize() {
+          return super.getPreferredSize();
+        }
+      };
+
+      myPanel = new JPanel(new BorderLayout());
+      myPanel.add(pane);
+
+      isFirstUpdate = false;
+    }
+    
+    @Override
+    public JComponent getPanel() {
+      return myPanel;
+    }
+
+    @Override
+    protected String getPreviewText() {
+      return null;
+    }
+  }
+
+  private class WrappingAndBracesPanelWithoutPreview extends MyWrappingAndBracesPanel {
+    public JPanel myPanel;
+
+    public WrappingAndBracesPanelWithoutPreview(CodeStyleSettings settings) {
+      super(settings);
+    }
+
+    @Override
+    protected void init() {
+      Collection<String> settingNames = mySettingsToShow.getSettings(getSettingsType());
+      initTables();
+
+      Collection<String> fields = populateWithAssociatedFields(settingNames);
+      fields.add("KEEP_LINE_BREAKS");
+
+      String[] names = ContainerUtil.toArray(fields, new String[fields.size()]);
+      showStandardOptions(names);
+
+      myTreeTable = createOptionsTree(getSettings());
+      JBScrollPane scrollPane = new JBScrollPane(myTreeTable) {
+        @Override
+        public Dimension getMinimumSize() {
+          return super.getPreferredSize();
+        }
+      };
+
+      myPanel = new JPanel(new BorderLayout());
+      myPanel.add(scrollPane);
+
+      showStandardOptions(names);
+
+      isFirstUpdate = false;
+    }
+
+    @NotNull
+    private Collection<String> populateWithAssociatedFields(Collection<String> settingNames) {
+      Set<String> commonFields = ContainerUtil.newHashSet();
+      for (String fieldName : settingNames) {
+        SettingsGroup settingsGroup = getAssociatedSettingsGroup(fieldName);
+        if (settingsGroup == null) {
+          commonFields.add(fieldName);
+        }
+        else if (settingsGroup.title != WRAPPING_KEEP) {
+          commonFields.addAll(settingsGroup.commonCodeStyleSettingFieldNames);
+        }
+      }
+      return commonFields;
+    }
+
+    @Override
+    public JComponent getPanel() {
+      return myPanel;
+    }
+    
+    @Override
+    protected void somethingChanged() {
+      mySelectedTextFormatter.restoreSelectedText();
+      reformatSelectedTextWithNewSettings();
+    }
+    
+    @Override
+    protected String getPreviewText() {
+      return null;
+    }
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/ConfigureCodeStyleOnSelectedFragment.java b/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/ConfigureCodeStyleOnSelectedFragment.java
new file mode 100644 (file)
index 0000000..a92e12b
--- /dev/null
@@ -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.formatting.contextConfiguration;
+
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.lang.Language;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.SelectionModel;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsCodeFragmentFilter;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+import static com.intellij.psi.codeStyle.CodeStyleSettingsCodeFragmentFilter.CodeStyleSettingsToShow;
+
+public class ConfigureCodeStyleOnSelectedFragment implements IntentionAction {
+  private static final Logger LOG = Logger.getInstance(ConfigureCodeStyleOnSelectedFragment.class);
+  
+  @Nls
+  @NotNull
+  @Override
+  public String getText() {
+    return "Configure code style";
+  }
+
+  @Nls
+  @NotNull
+  @Override
+  public String getFamilyName() {
+    return "ConfigureCodeStyleOnSelectedFragment";
+  }
+
+  @Override
+  public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
+    Language language = file.getLanguage();
+    return editor.getSelectionModel().hasSelection() && LanguageCodeStyleSettingsProvider.forLanguage(language) != null;
+  }
+
+  @Override
+  public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
+    SelectedTextFormatter textFormatter = new SelectedTextFormatter(project, editor, file);
+    CodeStyleSettingsToShow settingsToShow = calculateAffectingSettings(editor, file);
+    CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project);
+    new FragmentCodeStyleSettingsDialog(project, textFormatter, file.getLanguage(), settings, settingsToShow).show();
+  }
+
+  private static CodeStyleSettingsToShow calculateAffectingSettings(@NotNull Editor editor, @NotNull PsiFile file) {
+    SelectionModel model = editor.getSelectionModel();
+    int start = model.getSelectionStart();
+    int end = model.getSelectionEnd();
+    CodeStyleSettingsCodeFragmentFilter settingsProvider = new CodeStyleSettingsCodeFragmentFilter(file, new TextRange(start, end));
+    return CodeFragmentCodeStyleSettingsPanel.calcSettingNamesToShow(settingsProvider);
+  }
+
+  @Override
+  public boolean startInWriteAction() {
+    return false;
+  }
+  
+  static class FragmentCodeStyleSettingsDialog extends DialogWrapper {
+    private final CodeFragmentCodeStyleSettingsPanel myTabbedLanguagePanel;
+    private SelectedTextFormatter mySelectedTextFormatter;
+    private final CodeStyleSettings mySettings;
+
+    public FragmentCodeStyleSettingsDialog(@NotNull Project project,
+                                           @NotNull SelectedTextFormatter selectedTextFormatter,
+                                           @NotNull Language language,
+                                           CodeStyleSettings settings,
+                                           CodeStyleSettingsToShow settingsToShow) {
+      super(project, true);
+      mySettings = settings;
+      mySelectedTextFormatter = selectedTextFormatter;
+      myTabbedLanguagePanel = new CodeFragmentCodeStyleSettingsPanel(settings, settingsToShow, language, selectedTextFormatter);
+      
+      setTitle("Configure Code Style Settings: " + language.getDisplayName());
+      setOKButtonText("Save");
+      init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+      return myTabbedLanguagePanel.getPanel();
+    }
+
+    @Override
+    protected void dispose() {
+      super.dispose();
+      Disposer.dispose(myTabbedLanguagePanel);
+    }
+
+    @Override
+    protected void doOKAction() {
+      try {
+        myTabbedLanguagePanel.apply(mySettings);
+      }
+      catch (ConfigurationException e) {
+        LOG.debug("Can not apply code style settings from context menu to project code style settings");
+      }
+      super.doOKAction();
+    }
+
+    @Override
+    public void doCancelAction() {
+      mySelectedTextFormatter.restoreSelectedText();
+      super.doCancelAction();
+    }
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/SelectedTextFormatter.java b/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/SelectedTextFormatter.java
new file mode 100644 (file)
index 0000000..7344b4b
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * 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.formatting.contextConfiguration;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.SelectionModel;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import org.jetbrains.annotations.NotNull;
+
+public class SelectedTextFormatter {
+  private Project myProject;
+  private Editor myEditor;
+  private PsiFile myFile;
+
+  private final String myTextBefore;
+  private final int mySelectionStart;
+  private final int mySelectionEnd;
+
+
+  public SelectedTextFormatter(Project project, Editor editor, PsiFile file) {
+    myProject = project;
+    myEditor = editor;
+    myFile = file;
+
+    myTextBefore = myEditor.getSelectionModel().getSelectedText();
+    mySelectionStart = myEditor.getSelectionModel().getSelectionStart();
+    mySelectionEnd = myEditor.getSelectionModel().getSelectionEnd();
+  }
+
+  public void restoreSelectedText() {
+    final Document document = myEditor.getDocument();
+    final int start = myEditor.getSelectionModel().getSelectionStart();
+    final int end = myEditor.getSelectionModel().getSelectionEnd();
+
+    ApplicationManager.getApplication().runWriteAction(new Runnable() {
+      @Override
+      public void run() {
+        CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
+          @Override
+          public void run() {
+            document.replaceString(start, end, myTextBefore);
+          }
+        }, "Configure code style on selected fragment: restore text before", null);
+      }
+    });
+
+    myEditor.getSelectionModel().setSelection(mySelectionStart, mySelectionEnd);
+  }
+
+  void reformatSelectedText(@NotNull CodeStyleSettings reformatSettings) {
+    final SelectionModel model = myEditor.getSelectionModel();
+    if (model.hasSelection()) {
+      try {
+        CodeStyleSettingsManager.getInstance(myProject).setTemporarySettings(reformatSettings);
+        reformatRange(myFile, getSelectedRange());
+      }
+      finally {
+        CodeStyleSettingsManager.getInstance(myProject).dropTemporarySettings();
+      }
+    }
+  }
+
+  private static void reformatRange(final @NotNull PsiFile file, final @NotNull TextRange range) {
+    final Project project = file.getProject();
+    CommandProcessor.getInstance().executeCommand(project, new Runnable() {
+      @Override
+      public void run() {
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+          @Override
+          public void run() {
+            CodeStyleManager.getInstance(project).reformatText(file, range.getStartOffset(), range.getEndOffset());
+          }
+        });
+      }
+    }, "Reformat", null);
+  }
+
+  @NotNull
+  TextRange getSelectedRange() {
+    SelectionModel model = myEditor.getSelectionModel();
+    int start = model.getSelectionStart();
+    int end = model.getSelectionEnd();
+    return TextRange.create(start, end);
+  }
+}
\ No newline at end of file
diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/CodeStyleSettingsCodeFragmentFilter.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/CodeStyleSettingsCodeFragmentFilter.java
new file mode 100644 (file)
index 0000000..93975d7
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * 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.codeInsight.CodeInsightBundle;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.RangeMarker;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiFileFactory;
+import com.intellij.util.SequentialModalProgressTask;
+import com.intellij.util.SequentialTask;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.Field;
+import java.util.*;
+
+import static com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider.forLanguage;
+
+public class CodeStyleSettingsCodeFragmentFilter {
+  private static final Logger LOG = Logger.getInstance(CodeStyleSettingsCodeFragmentFilter.class);
+
+  private final Project myProject;
+  private final PsiFile myFile;
+  private final Document myDocument;
+  private final RangeMarker myTextRangeMarker;
+  private final LanguageCodeStyleSettingsProvider myProvider;
+
+  private CommonCodeStyleSettings myCommonSettings;
+
+  public CodeStyleSettingsCodeFragmentFilter(@NotNull PsiFile file, @NotNull TextRange range) {
+    myProvider = forLanguage(file.getLanguage());
+    myProject = file.getProject();
+    myFile =
+      PsiFileFactory.getInstance(myProject).createFileFromText("copy" + file.getName(), file.getLanguage(), file.getText(), true, false);
+    myDocument = PsiDocumentManager.getInstance(myProject).getDocument(myFile);
+    LOG.assertTrue(myDocument != null);
+    myTextRangeMarker = myDocument.createRangeMarker(range.getStartOffset(), range.getEndOffset());
+  }
+
+  @NotNull
+  public CodeStyleSettingsToShow getFieldNamesAffectingCodeFragment(LanguageCodeStyleSettingsProvider.SettingsType... types) {
+    CodeStyleSettingsManager codeStyleSettingsManager = CodeStyleSettingsManager.getInstance(myProject);
+    CodeStyleSettings clonedSettings = codeStyleSettingsManager.getCurrentSettings().clone();
+    myCommonSettings = clonedSettings.getCommonSettings(myProvider.getLanguage());
+
+    try {
+      codeStyleSettingsManager.setTemporarySettings(clonedSettings);
+
+      SequentialModalProgressTask progressTask = new SequentialModalProgressTask(myProject, CodeInsightBundle.message("configure.code.style.on.fragment.dialog.title"));
+      progressTask.setCancelText(CodeInsightBundle.message("configure.code.style.on.fragment.dialog.cancel"));
+      CompositeSequentialTask compositeTask = new CompositeSequentialTask(progressTask);
+      compositeTask.setProgressText(CodeInsightBundle.message("configure.code.style.on.fragment.dialog.progress.text"));
+      compositeTask.setProgressText2(CodeInsightBundle.message("configure.code.style.on.fragment.dialog.progress.text.under"));
+
+      final Map<LanguageCodeStyleSettingsProvider.SettingsType, FilterFieldsTask> typeToTask = ContainerUtil.newHashMap();
+      for (LanguageCodeStyleSettingsProvider.SettingsType type : types) {
+        Set<String> fields = myProvider.getSupportedFields(type);
+        FilterFieldsTask task = new FilterFieldsTask(fields);
+        compositeTask.addTask(task);
+        typeToTask.put(type, task);
+      }
+
+      progressTask.setTask(compositeTask);
+      progressTask.setMinIterationTime(10);
+      ProgressManager.getInstance().run(progressTask);
+
+      return new CodeStyleSettingsToShow() {
+        @Override
+        public List<String> getSettings(LanguageCodeStyleSettingsProvider.SettingsType type) {
+          FilterFieldsTask task = typeToTask.get(type);
+          return task.getAffectedFields();
+        }
+      };
+    }
+    finally {
+      codeStyleSettingsManager.dropTemporarySettings();
+    }
+  }
+
+  private boolean formattingChangedFragment() {
+    final int rangeStart = myTextRangeMarker.getStartOffset();
+    final int rangeEnd = myTextRangeMarker.getEndOffset();
+    CharSequence textBefore = myDocument.getCharsSequence();
+
+    ApplicationManager.getApplication().runWriteAction(new Runnable() {
+      @Override
+      public void run() {
+        CodeStyleManager.getInstance(myProject).reformatText(myFile, rangeStart, rangeEnd);
+      }
+    });
+
+    if (rangeStart != myTextRangeMarker.getStartOffset() || rangeEnd != myTextRangeMarker.getEndOffset()) {
+      return true;
+    }
+    else {
+      CharSequence fragmentBefore = textBefore.subSequence(rangeStart, rangeEnd);
+      CharSequence fragmentAfter = myDocument.getCharsSequence().subSequence(rangeStart, rangeEnd);
+      return !StringUtil.equals(fragmentBefore, fragmentAfter);
+    }
+  }
+
+  private class FilterFieldsTask implements SequentialTaskWithFixedIterationsNumber {
+    private final Iterator<String> myIterator;
+    private final int myTotalFieldsNumber;
+    private final Collection<String> myAllFields;
+
+    private List<String> myAffectingFields = ContainerUtil.newArrayList();
+
+    public FilterFieldsTask(@NotNull Collection<String> fields) {
+      myAllFields = fields;
+      myIterator = fields.iterator();
+      myTotalFieldsNumber = fields.size();
+    }
+
+    public List<String> getAffectedFields() {
+      return myAffectingFields;
+    }
+
+    @Override
+    public int getTotalIterationsNumber() {
+      return myTotalFieldsNumber;
+    }
+
+    @Override
+    public void stop() {
+      if (!isDone()) myAffectingFields = ContainerUtil.newArrayList(myAllFields);
+    }
+
+    @Override
+    public boolean isDone() {
+      return !myIterator.hasNext();
+    }
+
+    @Override
+    public boolean iteration() {
+      if (!myIterator.hasNext()) return true;
+
+      String field = myIterator.next();
+      try {
+        Field classField = CommonCodeStyleSettings.class.getField(field);
+
+        if (classField.getType() == Integer.TYPE) {
+          int oldValue = classField.getInt(myCommonSettings);
+          int newValue = getNewIntValue(classField, oldValue);
+          if (newValue == oldValue) {
+            return true;
+          }
+          classField.set(myCommonSettings, newValue);
+        }
+        else if (classField.getType() == Boolean.TYPE) {
+          boolean value = classField.getBoolean(myCommonSettings);
+          classField.set(myCommonSettings, !value);
+        }
+        else {
+          return true;
+        }
+
+        if (formattingChangedFragment()) {
+          myAffectingFields.add(field);
+        }
+      }
+      catch (Exception ignored) {
+      }
+
+      return true;
+    }
+
+    private int getNewIntValue(Field classField, int oldValue) throws IllegalAccessException {
+      int newValue = oldValue;
+
+      String fieldName = classField.getName();
+      if (fieldName.contains("WRAP")) {
+        newValue = oldValue == CommonCodeStyleSettings.WRAP_ALWAYS
+                   ? CommonCodeStyleSettings.DO_NOT_WRAP
+                   : CommonCodeStyleSettings.WRAP_ALWAYS;
+      }
+      else if (fieldName.contains("BRACE")) {
+        newValue = oldValue == CommonCodeStyleSettings.FORCE_BRACES_ALWAYS
+                   ? CommonCodeStyleSettings.DO_NOT_FORCE
+                   : CommonCodeStyleSettings.WRAP_ALWAYS;
+      }
+
+      return newValue;
+    }
+
+    @Override
+    public void prepare() {
+    }
+  }
+
+  public interface CodeStyleSettingsToShow {
+    List<String> getSettings(LanguageCodeStyleSettingsProvider.SettingsType type);
+  }
+}
+
+interface SequentialTaskWithFixedIterationsNumber extends SequentialTask {
+  int getTotalIterationsNumber();
+}
+
+class CompositeSequentialTask implements SequentialTask {
+  private List<SequentialTaskWithFixedIterationsNumber> myUnfinishedTasks = ContainerUtil.newArrayList();
+  private SequentialTask myCurrentTask = null;
+
+  private int myIterationsFinished;
+  private int myTotalIterations = 0;
+
+  private final SequentialModalProgressTask myProgressTask;
+  private String myProgressText;
+  private String myProgressText2;
+
+  public CompositeSequentialTask(@NotNull SequentialModalProgressTask progressTask) {
+    myProgressTask = progressTask;
+  }
+
+  public void addTask(@NotNull SequentialTaskWithFixedIterationsNumber task) {
+    myUnfinishedTasks.add(task);
+    myTotalIterations += task.getTotalIterationsNumber();
+  }
+
+  public void setProgressText(@NotNull String progressText) {
+    myProgressText = progressText;
+  }
+
+  @Override
+  public boolean isDone() {
+    return myCurrentTask == null && myUnfinishedTasks.size() == 0;
+  }
+
+  @Override
+  public boolean iteration() {
+    popUntilCurrentTaskUnfinishedOrNull();
+
+    if (myCurrentTask != null) {
+      ProgressIndicator indicator = myProgressTask.getIndicator();
+      if (indicator != null) {
+        if (myProgressText != null) indicator.setText(myProgressText);
+        if (myProgressText2 != null) indicator.setText2(myProgressText2);
+        indicator.setFraction((double)myIterationsFinished++ / myTotalIterations);
+      }
+      myCurrentTask.iteration();
+    }
+
+    return true;
+  }
+
+  private void popUntilCurrentTaskUnfinishedOrNull() {
+    if (myCurrentTask != null) {
+      if (!myCurrentTask.isDone()) {
+        return;
+      }
+      myCurrentTask = null;
+      popUntilCurrentTaskUnfinishedOrNull();
+    }
+    else {
+      if (myUnfinishedTasks.size() > 0) {
+        myCurrentTask = myUnfinishedTasks.get(0);
+        myUnfinishedTasks.remove(0);
+        popUntilCurrentTaskUnfinishedOrNull();
+      }
+    }
+  }
+
+  @Override
+  public void prepare() {
+  }
+
+  @Override
+  public void stop() {
+    if (myCurrentTask != null) myCurrentTask.stop();
+    for (SequentialTaskWithFixedIterationsNumber task : myUnfinishedTasks) {
+      task.stop();
+    }
+  }
+
+  public void setProgressText2(String progressText2) {
+    myProgressText2 = progressText2;
+  }
+}
index 56529723a58eff30fca6b0b3e5fdfad50f7b5f2f..fe69d4e77ee243bbc847de3d1afb0b71dfdddecf 100644 (file)
@@ -149,11 +149,11 @@ public class JBComboBoxTableCellEditorComponent extends JBLabel {
       .setItemChoosenCallback(new Runnable() {
         @Override
         public void run() {
+          myValue = myList.getSelectedValue();
           final ActionEvent event = new ActionEvent(myList, ActionEvent.ACTION_PERFORMED, "elementChosen");
           for (ActionListener listener : myListeners) {
             listener.actionPerformed(event);
           }
-          myValue = myList.getSelectedValue();
           TableUtil.stopEditing(myTable);
 
           myTable.setValueAt(myValue, myRow, myColumn); // on Mac getCellEditorValue() called before myValue is set.
diff --git a/platform/platform-resources-en/src/intentionDescriptions/ConfigureCodeStyleOnSelectedFragment/after.txt.template b/platform/platform-resources-en/src/intentionDescriptions/ConfigureCodeStyleOnSelectedFragment/after.txt.template
new file mode 100644 (file)
index 0000000..cca8d7e
--- /dev/null
@@ -0,0 +1,3 @@
+public class NewClass
+{
+}
\ No newline at end of file
diff --git a/platform/platform-resources-en/src/intentionDescriptions/ConfigureCodeStyleOnSelectedFragment/before.txt.template b/platform/platform-resources-en/src/intentionDescriptions/ConfigureCodeStyleOnSelectedFragment/before.txt.template
new file mode 100644 (file)
index 0000000..162ce7f
--- /dev/null
@@ -0,0 +1,2 @@
+public class NewClass {
+}
\ No newline at end of file
diff --git a/platform/platform-resources-en/src/intentionDescriptions/ConfigureCodeStyleOnSelectedFragment/description.html b/platform/platform-resources-en/src/intentionDescriptions/ConfigureCodeStyleOnSelectedFragment/description.html
new file mode 100644 (file)
index 0000000..bca0202
--- /dev/null
@@ -0,0 +1,5 @@
+<html>
+<body>
+Shows code style settings affecting currently selected code fragment in editor
+</body>
+</html>
\ No newline at end of file
index 2535e21b76be7361fbd5da83e367505cc89ec636..86141c214bbd7985cf65fac6d13beb2d23b00e36 100644 (file)
@@ -19,6 +19,10 @@ reformat.option.vcs.changed.region=Only &VCS changed text
 reformat.progress.file.with.known.name.text=Reformatting {0}
 reformat.and.optimize.progress.common.text=Preparing imports...
 reformat.progress.common.text=Reformatting code...
+configure.code.style.on.fragment.dialog.title=Configure Code Style
+configure.code.style.on.fragment.dialog.cancel=Skip
+configure.code.style.on.fragment.dialog.progress.text=Filtering settings affecting selected code fragment...
+configure.code.style.on.fragment.dialog.progress.text.under=Press 'Skip' to show all settings
 process.optimize.imports=Optimize Imports
 process.optimize.imports.before.commit=Optimize Imports Before Commit
 progress.text.optimizing.imports=Optimizing imports...
index d7a15abbbeab5867013af2931e5663ca2a276fd3..e84b6d000fa34a4e0d1406a7fed70b2802583d2d 100644 (file)
     <intentionAction>
       <className>com.intellij.codeInsight.intention.impl.EditFoldingOptionsAction</className>
     </intentionAction>
+    <intentionAction>
+      <className>com.intellij.formatting.contextConfiguration.ConfigureCodeStyleOnSelectedFragment</className>
+    </intentionAction>
     <intentionAction>
       <className>com.intellij.codeInsight.intention.impl.QuickEditAction</className>
       <category>Language Injection</category>