Merge branch 'no-reformat-dialog-on-reformat-code-action'
authorYaroslav Lepenkin <yaroslav.lepenkin@jetbrains.com>
Fri, 20 Feb 2015 14:15:21 +0000 (17:15 +0300)
committerYaroslav Lepenkin <yaroslav.lepenkin@jetbrains.com>
Fri, 20 Feb 2015 14:15:21 +0000 (17:15 +0300)
56 files changed:
java/java-impl/src/com/intellij/lang/java/JavaImportOptimizer.java
java/java-tests/testData/actions/reformatFileInEditor/selectedTextAndOptimizeImports_after.java [new file with mode: 0644]
java/java-tests/testData/actions/reformatFileInEditor/selectedTextAndOptimizeImports_before.java [new file with mode: 0644]
java/java-tests/testData/actions/reformatFileInEditor/selectionReformat_after.java [new file with mode: 0644]
java/java-tests/testData/actions/reformatFileInEditor/selectionReformat_before.java [new file with mode: 0644]
java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformatAndOptimize_after.java [new file with mode: 0644]
java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformatAndOptimize_before.java [new file with mode: 0644]
java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformat_after.java [new file with mode: 0644]
java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformat_before.java [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessorTest.java
java/java-tests/testSrc/com/intellij/codeInsight/actions/ReformatCodeActionInEditorTest.java
java/java-tests/testSrc/com/intellij/codeInsight/actions/ReformatCodeActionTest.java
platform/lang-api/src/com/intellij/lang/ImportOptimizer.java
platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/RearrangeCodeAction.java
platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java
platform/lang-impl/src/com/intellij/codeInsight/actions/FileInEditorProcessor.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/actions/FormatChangedTextUtil.java
platform/lang-impl/src/com/intellij/codeInsight/actions/LastRunReformatCodeOptionsProvider.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeDialog.form [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeDialog.java
platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeInfoCollector.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeOptions.java
platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeSettingsStorage.java [deleted file]
platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutProjectCodeDialog.java
platform/lang-impl/src/com/intellij/codeInsight/actions/OptimizeImportsAction.java
platform/lang-impl/src/com/intellij/codeInsight/actions/OptimizeImportsProcessor.java
platform/lang-impl/src/com/intellij/codeInsight/actions/OptionalReformatActions.java [moved from platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeConstants.java with 54% similarity]
platform/lang-impl/src/com/intellij/codeInsight/actions/RearrangeCodeProcessor.java
platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeAction.java
platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeProcessor.java
platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeRunOptions.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatFilesDialog.java
platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatFilesOptions.java
platform/lang-impl/src/com/intellij/codeInsight/actions/ShowReformatFileDialog.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/psi/codeStyle/arrangement/engine/ArrangementEngine.java
platform/platform-resources-en/src/messages/ActionsBundle.properties
platform/platform-resources-en/src/messages/CodeInsightBundle.properties
platform/platform-resources/src/idea/Keymap_Default.xml
platform/platform-resources/src/idea/LangActions.xml
platform/platform-tests/testData/codeStyle/formatter/addedLines.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/addedLines_revision.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/changedAndDeleted.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/changedAndDeleted_revision.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/changedSingleLine.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/changedSingleLine_revision.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/deletedLines.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/deletedLines_revision.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/insert.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/insert_revision.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/lotsWhiteSpaces.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/lotsWhiteSpaces_revision.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/modification.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/modification_revision.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/modifiedLines.java [new file with mode: 0644]
platform/platform-tests/testData/codeStyle/formatter/modifiedLines_revision.java [new file with mode: 0644]
platform/platform-tests/testSrc/com/intellij/codeInsight/actions/ChangedLinesCounterTest.java [new file with mode: 0644]

index bd2d1a9a29fc3a6abe10f32743e5507b5e163eba..eddd536d19fbacc08ac4dec47932f7e235b9e490 100644 (file)
@@ -28,6 +28,7 @@ import com.intellij.psi.PsiJavaFile;
 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
 import com.intellij.util.IncorrectOperationException;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * @author max
@@ -44,7 +45,10 @@ public class JavaImportOptimizer implements ImportOptimizer {
     Project project = file.getProject();
     final PsiImportList newImportList = JavaCodeStyleManager.getInstance(project).prepareOptimizeImportsResult((PsiJavaFile)file);
     if (newImportList == null) return EmptyRunnable.getInstance();
-    return new Runnable() {
+
+    return new CollectingInfoRunnable() {
+      private int myImportListLengthDiff = 0;
+
       @Override
       public void run() {
         try {
@@ -55,12 +59,23 @@ public class JavaImportOptimizer implements ImportOptimizer {
           }
           final PsiImportList oldImportList = ((PsiJavaFile)file).getImportList();
           assert oldImportList != null;
+          int importsBefore = oldImportList.getAllImportStatements().length;
           oldImportList.replace(newImportList);
+          myImportListLengthDiff = importsBefore - newImportList.getAllImportStatements().length;
         }
         catch (IncorrectOperationException e) {
           LOG.error(e);
         }
       }
+
+      @Nullable
+      @Override
+      public String getUserNotificationInfo() {
+        if (myImportListLengthDiff > 0) {
+          return "removed " + myImportListLengthDiff + " import" + (myImportListLengthDiff > 1 ? "s" : "");
+        }
+        return null;
+      }
     };
   }
 
diff --git a/java/java-tests/testData/actions/reformatFileInEditor/selectedTextAndOptimizeImports_after.java b/java/java-tests/testData/actions/reformatFileInEditor/selectedTextAndOptimizeImports_after.java
new file mode 100644 (file)
index 0000000..edb36d6
--- /dev/null
@@ -0,0 +1,14 @@
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class Test {
+
+        public static void main(String[] args) {
+            List<String> list = new ArrayList<String>();
+            int a = 3;
+
+int b = 3;
+        }
+
+}
diff --git a/java/java-tests/testData/actions/reformatFileInEditor/selectedTextAndOptimizeImports_before.java b/java/java-tests/testData/actions/reformatFileInEditor/selectedTextAndOptimizeImports_before.java
new file mode 100644 (file)
index 0000000..2256d47
--- /dev/null
@@ -0,0 +1,17 @@
+import java.lang.String;
+import java.util.List;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+
+public class Test {
+
+        public static void main(String[] args) {
+<selection>List<String> list = new ArrayList<String>();
+int a = 3;</selection>
+
+int b = 3;
+        }
+
+}
diff --git a/java/java-tests/testData/actions/reformatFileInEditor/selectionReformat_after.java b/java/java-tests/testData/actions/reformatFileInEditor/selectionReformat_after.java
new file mode 100644 (file)
index 0000000..7e145c3
--- /dev/null
@@ -0,0 +1,11 @@
+public class Test {
+
+    int a = 2;
+    int b = 3;
+
+
+         public void run() {
+               int myFirst = 12;
+            }
+
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/actions/reformatFileInEditor/selectionReformat_before.java b/java/java-tests/testData/actions/reformatFileInEditor/selectionReformat_before.java
new file mode 100644 (file)
index 0000000..d753fc6
--- /dev/null
@@ -0,0 +1,11 @@
+public class Test {
+
+      <selection>int a = 2;
+           int b = 3;</selection>
+
+
+         public void run() {
+               int myFirst = 12;
+            }
+
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformatAndOptimize_after.java b/java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformatAndOptimize_after.java
new file mode 100644 (file)
index 0000000..b3c3407
--- /dev/null
@@ -0,0 +1,11 @@
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class Test {
+
+    public static void main(String[] args) {
+        List<String> list = new ArrayList<String>();
+    }
+
+}
diff --git a/java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformatAndOptimize_before.java b/java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformatAndOptimize_before.java
new file mode 100644 (file)
index 0000000..a996d55
--- /dev/null
@@ -0,0 +1,14 @@
+import java.lang.String;
+import java.util.List;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+
+public class Test {
+
+        public static void main(String[] args) {
+List<String> list = new ArrayList<String>();
+  }
+
+}
diff --git a/java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformat_after.java b/java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformat_after.java
new file mode 100644 (file)
index 0000000..05460c5
--- /dev/null
@@ -0,0 +1,10 @@
+public class Test {
+
+    public int a = 3;
+
+
+    public void test() {
+        int c = 3;
+    }
+
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformat_before.java b/java/java-tests/testData/actions/reformatFileInEditor/wholeFileReformat_before.java
new file mode 100644 (file)
index 0000000..e8d0ad4
--- /dev/null
@@ -0,0 +1,10 @@
+public class Test {
+
+        public int a = 3;
+
+
+            public void test() {
+                       int c = 3;
+                }
+
+}
\ No newline at end of file
index 366fd626baaf8a829c8d3d3df2656a7188940039..b780665d1c63796681f21a86fa6f492b678e084d 100644 (file)
@@ -316,14 +316,9 @@ class AdditionalEventInfo {
   }
 }
 
-class MockReformatFileSettings implements LayoutCodeOptions {
-  private boolean myProcessWholeFile;
-  private boolean myProcessDirectories;
-  private boolean myRearrange;
-  private boolean myIncludeSubdirs;
+class MockReformatFileSettings implements ReformatFilesOptions {
   private boolean myOptimizeImports;
-  private boolean myProcessOnlyChangedText;
-  private boolean myIsOK = true;
+  private boolean myIncludeSubdirs;
 
   @Nullable
   @Override
@@ -338,33 +333,13 @@ class MockReformatFileSettings implements LayoutCodeOptions {
   }
 
   @Override
-  public boolean isProcessWholeFile() {
-    return myProcessWholeFile;
-  }
-
-  MockReformatFileSettings setProcessWholeFile(boolean processWholeFile) {
-    myProcessWholeFile = processWholeFile;
-    return this;
+  public TextRangeType getTextRangeType() {
+    return TextRangeType.WHOLE_FILE;
   }
 
   @Override
-  public boolean isProcessDirectory() {
-    return myProcessDirectories;
-  }
-
-  MockReformatFileSettings setProcessDirectory(boolean processDirectories) {
-    myProcessDirectories = processDirectories;
-    return this;
-  }
-
-  @Override
-  public boolean isRearrangeEntries() {
-    return myRearrange;
-  }
-
-  @Override
-  public boolean isIncludeSubdirectories() {
-    return myIncludeSubdirs;
+  public boolean isRearrangeCode() {
+    return false;
   }
 
   @Override
@@ -378,23 +353,6 @@ class MockReformatFileSettings implements LayoutCodeOptions {
     return this;
   }
 
-  @Override
-  public boolean isProcessOnlyChangedText() {
-    return myProcessOnlyChangedText;
-  }
-
-  @NotNull
-  MockReformatFileSettings setProcessOnlyChangedText(boolean processOnlyChangedText) {
-    myProcessOnlyChangedText = processOnlyChangedText;
-    return this;
-  }
-
-  @NotNull
-  MockReformatFileSettings setRearrange(boolean rearrange) {
-    myRearrange = rearrange;
-    return this;
-  }
-
   @NotNull
   MockReformatFileSettings setIncludeSubdirs(boolean includeSubdirs) {
     myIncludeSubdirs = includeSubdirs;
index 0dd2bcebc27d8adf486598b66d3e7df127fd62c5..058d165c3ddf158c16457f4244d02eb6d56cb8f5 100644 (file)
 package com.intellij.codeInsight.actions;
 
 import com.intellij.JavaTestUtil;
-import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vcs.FilePath;
-import com.intellij.openapi.vcs.VcsException;
-import com.intellij.openapi.vcs.changes.Change;
-import com.intellij.openapi.vcs.changes.ContentRevision;
-import com.intellij.openapi.vcs.history.VcsRevisionNumber;
 import com.intellij.psi.PsiFile;
-import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
 
-public class ReformatCodeActionInEditorTest extends LightCodeInsightFixtureTestCase {
+import static com.intellij.codeInsight.actions.TextRangeType.*;
+
+public class ReformatCodeActionInEditorTest extends LightPlatformCodeInsightFixtureTestCase {
 
   @Override
   protected String getTestDataPath() {
     return JavaTestUtil.getJavaTestDataPath() + "/actions/reformatFileInEditor/";
   }
 
-  public void doTest(@NotNull ReformatFilesOptions options) {
-    setOptions(options);
+  @Override
+  public void tearDown() throws Exception {
+    myFixture.getFile().putUserData(FormatChangedTextUtil.TEST_REVISION_CONTENT, null);
+    super.tearDown();
+  }
 
-    String before = null;
-    if (options.isProcessOnlyChangedText()) {
+  public void doTest(LayoutCodeOptions options) {
+    CharSequence revisionContent = null;
+    if (options.getTextRangeType() == VCS_CHANGED_TEXT) {
       myFixture.configureByFile(getTestName(true) + "_revision.java");
       PsiFile file = myFixture.getFile();
       Document document = myFixture.getDocument(file);
-      before = document.getText();
+      revisionContent = document.getCharsSequence();
     }
 
     myFixture.configureByFile(getTestName(true) + "_before.java");
-
-    if (before != null) {
-      myFixture.getFile().putUserData(FormatChangedTextUtil.TEST_REVISION_CONTENT, before);
+    if (revisionContent != null) {
+      myFixture.getFile().putUserData(FormatChangedTextUtil.TEST_REVISION_CONTENT, revisionContent);
     }
 
-    final String actionId = IdeActions.ACTION_EDITOR_REFORMAT;
-    AnAction action = ActionManager.getInstance().getAction(actionId);
-
-    AnActionEvent event = createEventFor(action, getProject(), myFixture.getEditor());
+    FileInEditorProcessor processor = new FileInEditorProcessor(myFixture.getFile(), myFixture.getEditor(), options);
 
-    action.actionPerformed(event);
+    processor.processCode();
     myFixture.checkResultByFile(getTestName(true) + "_after.java");
   }
 
-  @Override
-  public void tearDown() throws Exception {
-    myFixture.getFile().putUserData(FormatChangedTextUtil.TEST_REVISION_CONTENT, null);
-    super.tearDown();
+  public void testSelectionReformat() {
+    doTest(new ReformatCodeRunOptions(SELECTED_TEXT));
   }
 
-  protected AnActionEvent createEventFor(@NotNull AnAction action, @NotNull final Project project, @NotNull final Editor editor) {
-    return new AnActionEvent(null, new DataContext() {
-      @Nullable
-      @Override
-      public Object getData(@NonNls String dataId) {
-        if (CommonDataKeys.PROJECT.is(dataId)) return project;
-        if (CommonDataKeys.EDITOR.is(dataId)) return editor;
-        return null;
-      }
-    }, "", action.getTemplatePresentation(), ActionManager.getInstance(), 0);
+  public void testWholeFileReformat() {
+    doTest(new ReformatCodeRunOptions(WHOLE_FILE));
   }
 
-  protected void setOptions(ReformatFilesOptions options) {
-    ReformatCodeAction.setTestOptions(options);
+  public void testWholeFileReformatAndOptimize() {
+    doTest(new ReformatCodeRunOptions(WHOLE_FILE).setOptimizeImports(true));
+  }
+
+  public void testSelectedTextAndOptimizeImports() {
+    doTest(new ReformatCodeRunOptions(SELECTED_TEXT).setOptimizeImports(true));
   }
 
   public void testFormatWholeFile() {
-    doTest(new MockReformatFileSettings().setProcessWholeFile(true));
+    doTest(new ReformatCodeRunOptions(WHOLE_FILE));
   }
 
   public void testFormatOptimizeWholeFile() {
-    doTest(new MockReformatFileSettings().setProcessWholeFile(true).setOptimizeImports(true));
+    doTest(new ReformatCodeRunOptions(WHOLE_FILE).setOptimizeImports(true));
   }
 
   public void testFormatOptimizeRearrangeWholeFile() {
-    doTest(new MockReformatFileSettings().setProcessWholeFile(true).setOptimizeImports(true).setRearrange(true));
+    doTest(new ReformatCodeRunOptions(WHOLE_FILE).setOptimizeImports(true).setRearrangeCode(true));
   }
 
   public void testFormatSelection() {
-    doTest(new MockReformatFileSettings().setProcessWholeFile(false));
+    doTest(new ReformatCodeRunOptions(SELECTED_TEXT));
   }
 
   public void testFormatRearrangeSelection() {
-    doTest(new MockReformatFileSettings().setProcessWholeFile(false).setRearrange(true));
+    doTest(new ReformatCodeRunOptions(SELECTED_TEXT).setRearrangeCode(true));
   }
 
   public void testFormatVcsChanges() {
-    doTest(new MockReformatFileSettings().setProcessOnlyChangedText(true));
+    doTest(new ReformatCodeRunOptions(VCS_CHANGED_TEXT));
   }
 
   public void testFormatOptimizeVcsChanges() {
-    doTest(new MockReformatFileSettings().setProcessOnlyChangedText(true).setOptimizeImports(true));
+    doTest(new ReformatCodeRunOptions(VCS_CHANGED_TEXT).setOptimizeImports(true));
   }
 
   public void testFormatOptimizeRearrangeVcsChanges() {
-    doTest(new MockReformatFileSettings().setProcessOnlyChangedText(true).setOptimizeImports(true).setRearrange(true));
+    doTest(new ReformatCodeRunOptions(VCS_CHANGED_TEXT).setOptimizeImports(true).setRearrangeCode(true));
   }
-
 }
index b0e4024ff076fe28602f9dc5b65ee55892efe9d2..09dff801a2f531915c8962a24dccb9d4df39b8a1 100644 (file)
@@ -35,35 +35,10 @@ public class ReformatCodeActionTest extends AbstractLayoutCodeProcessorTest {
   public void testReformatAndOptimizeMultipleFiles() throws IOException {
     List<PsiFile> files = createTestFiles(getTempRootDirectory(), classNames);
     injectMockDialogFlags(new MockReformatFileSettings().setOptimizeImports(true));
-
     performReformatActionOnSelectedFiles(files);
     checkFormationAndImportsOptimizationFor(files);
   }
 
-  public void testReformatSingleSelectedFile() throws IOException {
-    List<PsiFile> files = createTestFiles(getTempRootDirectory(), classNames);
-    PsiFile fileToReformat = files.get(0);
-    List<PsiFile> shouldNotBeFormatted = files.subList(1, files.size());
-    injectMockDialogFlags(new MockReformatFileSettings().setOptimizeImports(true));
-
-    performReformatActionOnSelectedFile(fileToReformat);
-
-    checkFormationAndImportsOptimizationFor(Arrays.asList(fileToReformat));
-    checkNoProcessingWasPerformedOn(shouldNotBeFormatted);
-  }
-
-  public void testReformatAndOptimizeFileFromEditor() throws IOException {
-    List<PsiFile> files = createTestFiles(getTempRootDirectory(), classNames);
-    injectMockDialogFlags(new MockReformatFileSettings().setOptimizeImports(true));
-
-    PsiFile fileToFormat = files.get(0);
-    List<PsiFile> shouldNotBeFormatted = files.subList(1, files.size());
-
-    performReformatActionOnFileInEditor(fileToFormat);
-    checkFormationAndImportsOptimizationFor(Arrays.asList(fileToFormat));
-    checkNoProcessingWasPerformedOn(shouldNotBeFormatted);
-  }
-
   public void testOptimizeAndReformatOnlySelectedFiles() throws IOException {
     List<PsiFile> files = createTestFiles(getTempRootDirectory(), classNames);
     List<PsiFile> forProcessing = ContainerUtil.newArrayList(files.get(0), files.get(1));
@@ -77,28 +52,6 @@ public class ReformatCodeActionTest extends AbstractLayoutCodeProcessorTest {
     checkNoProcessingWasPerformedOn(noProcessing);
   }
 
-  public void testOptimizeAndReformatAllFilesInDirectoryIncludeSubdirs() throws IOException {
-    TestFileStructure fileStructure = getThreeLevelDirectoryStructure();
-    injectMockDialogFlags(new MockReformatFileSettings().setOptimizeImports(true).setProcessDirectory(true).setIncludeSubdirs(true));
-    PsiFile fileInEditor = fileStructure.getFilesAtLevel(2).get(0);
-    List<PsiFile> shouldNotBeFormatted = fileStructure.getFilesAtLevel(1);
-
-    performReformatActionOnFileInEditor(fileInEditor);
-
-    checkFormationAndImportsOptimizationFor(fileStructure.getFilesAtLevel(2), fileStructure.getFilesAtLevel(3));
-    checkNoProcessingWasPerformedOn(shouldNotBeFormatted);
-  }
-
-  public void testOptimizeAndReformatAllFilesInDirectoryExcludeSubdirs() throws IOException {
-    TestFileStructure fileStructure = getThreeLevelDirectoryStructure();
-    injectMockDialogFlags(new MockReformatFileSettings().setOptimizeImports(true).setIncludeSubdirs(false));
-
-    performReformatActionOnSelectedFiles(fileStructure.getFilesAtLevel(2));
-
-    checkFormationAndImportsOptimizationFor(fileStructure.getFilesAtLevel(2));
-    checkNoProcessingWasPerformedOn(fileStructure.getFilesAtLevel(1), fileStructure.getFilesAtLevel(3));
-  }
-
   public void testOptimizeAndReformatInModule() throws IOException {
     Module module = createModuleWithSourceRoot("newModule");
     VirtualFile srcDir = ModuleRootManager.getInstance(module).getSourceRoots()[0];
@@ -109,26 +62,4 @@ public class ReformatCodeActionTest extends AbstractLayoutCodeProcessorTest {
 
     checkFormationAndImportsOptimizationFor(files);
   }
-
-  private TestFileStructure getThreeLevelDirectoryStructure() throws IOException {
-    TestFileStructure fileStructure = new TestFileStructure(getModule(), getTempRootDirectory());
-
-    fileStructure.createDirectoryAndMakeItCurrent("dir");
-    addFilesToCurrentDirectory(fileStructure);
-
-    fileStructure.createDirectoryAndMakeItCurrent("innerDir");
-    addFilesToCurrentDirectory(fileStructure);
-
-    fileStructure.createDirectoryAndMakeItCurrent("innerInnerDir");
-    addFilesToCurrentDirectory(fileStructure);
-
-    return fileStructure;
-  }
-
-  private void addFilesToCurrentDirectory(TestFileStructure fileStructure) throws IOException {
-    for (int i = 0; i < 5; i++) {
-      String className = "Test" + i;
-      fileStructure.addTestFile("Test" + i + ".java", getUntouchedJavaSourceForTotalProcessing(className));
-    }
-  }
 }
index 2e2f12af1d0f4663b8144ab82db62a54b5ad83f2..4aca7d3abec8437e4fc758798d9c38ff9edd6cb5 100644 (file)
@@ -18,6 +18,7 @@ package com.intellij.lang;
 
 import com.intellij.psi.PsiFile;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Implementers of the interface encapsulate optimize imports process for the language.
@@ -50,4 +51,11 @@ public interface ImportOptimizer {
   @NotNull
   Runnable processFile(PsiFile file);
 
+  /**
+   * In order to customize notification popup after reformat code action just return it from {@link #processFile} with proper information,
+   * by default "imports optimized" is shown.
+   */
+  interface CollectingInfoRunnable extends Runnable {
+    @Nullable String getUserNotificationInfo();
+  }
 }
index 843f8880d713122fa330e37e99bcb79a08acec4a..959bcf75e7d21c86432d76462f949595348e5883 100644 (file)
@@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 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.psi.PsiDocumentManager;
 import com.intellij.psi.PsiFile;
@@ -61,6 +62,13 @@ public class RearrangeCodeAction extends AnAction {
     if (file == null) {
       return;
     }
-    new RearrangeCodeProcessor(project, file, editor.getSelectionModel()).run();
+
+    SelectionModel model = editor.getSelectionModel();
+    if (model.hasSelection()) {
+      new RearrangeCodeProcessor(file, model).run();
+    }
+    else {
+      new RearrangeCodeProcessor(file).run();
+    }
   }
 }
index e2447f775f6a4da0f8b1e278b7d295f1235b79be..5613b489e71ca940d7571969ff730e5fe9426acd 100644 (file)
@@ -18,12 +18,16 @@ package com.intellij.codeInsight.actions;
 
 import com.intellij.codeInsight.CodeInsightBundle;
 import com.intellij.lang.LanguageFormatting;
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.application.ApplicationBundle;
 import com.intellij.openapi.application.ApplicationBundle;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.command.CommandProcessor;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.SelectionModel;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.progress.ProcessCanceledException;
@@ -37,6 +41,7 @@ import com.intellij.openapi.project.ProjectUtil;
 import com.intellij.openapi.roots.GeneratedSourcesFilter;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.ui.ex.MessagesEx;
+import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiBundle;
 import com.intellij.psi.PsiDirectory;
@@ -45,7 +50,9 @@ import com.intellij.psi.PsiFile;
 import com.intellij.util.IncorrectOperationException;
 import com.intellij.util.SequentialModalProgressTask;
 import com.intellij.util.SequentialTask;
+import com.intellij.util.SmartList;
 import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.diff.FilesTooBigForDiffException;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -69,12 +76,14 @@ public abstract class AbstractLayoutCodeProcessor {
 
   private final String myProgressText;
   private final String myCommandName;
-  private final Runnable myPostRunnable;
+  private Runnable myPostRunnable;
   private boolean myProcessChangedTextOnly;
 
   protected AbstractLayoutCodeProcessor myPreviousCodeProcessor;
   private List<FileFilter> myFilters = ContainerUtil.newArrayList();
 
+  private LayoutCodeInfoCollector myInfoCollector;
+
   protected AbstractLayoutCodeProcessor(Project project, String commandName, String progressText, boolean processChangedTextOnly) {
     this(project, (Module)null, commandName, progressText, processChangedTextOnly);
   }
@@ -96,6 +105,7 @@ public abstract class AbstractLayoutCodeProcessor {
     myCommandName = commandName;
     myPreviousCodeProcessor = previous;
     myFilters = previous.myFilters;
+    myInfoCollector = previous.myInfoCollector;
   }
 
   protected AbstractLayoutCodeProcessor(Project project,
@@ -171,12 +181,26 @@ public abstract class AbstractLayoutCodeProcessor {
     return list;
   }
 
+  public void setPostRunnable(Runnable postRunnable) {
+    myPostRunnable = postRunnable;
+  }
+
   @Nullable
   private FutureTask<Boolean> getPreviousProcessorTask(@NotNull PsiFile file, boolean processChangedTextOnly) {
     return myPreviousCodeProcessor != null ? myPreviousCodeProcessor.preprocessFile(file, processChangedTextOnly)
                                            : null;
   }
 
+  public void setCollectInfo(boolean isCollectInfo) {
+    myInfoCollector = isCollectInfo ? new LayoutCodeInfoCollector() : null;
+
+    AbstractLayoutCodeProcessor current = this;
+    while (current.myPreviousCodeProcessor != null) {
+      current = current.myPreviousCodeProcessor;
+      current.myInfoCollector = myInfoCollector;
+    }
+  }
+
   public void addFileFilter(@NotNull FileFilter filter) {
     myFilters.add(filter);
   }
@@ -584,4 +608,37 @@ public abstract class AbstractLayoutCodeProcessor {
 
     return true;
   }
+
+  protected static List<TextRange> getSelectedRanges(@NotNull SelectionModel selectionModel) {
+    final List<TextRange> ranges = new SmartList<TextRange>();
+    if (selectionModel.hasSelection()) {
+      TextRange range = TextRange.create(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
+      ranges.add(range);
+    }
+    else if (selectionModel.hasBlockSelection()) {
+      int[] starts = selectionModel.getBlockSelectionStarts();
+      int[] ends = selectionModel.getBlockSelectionEnds();
+      for (int i = 0; i < starts.length; i++) {
+        ranges.add(TextRange.create(starts[i], ends[i]));
+      }
+    }
+
+    return ranges;
+  }
+
+  protected void handleFileTooBigException(Logger logger, FilesTooBigForDiffException e, @NotNull PsiFile file) {
+    logger.info("Error while calculating changed ranges for: " + file.getVirtualFile(), e);
+    if (!ApplicationManager.getApplication().isUnitTestMode()) {
+      Notification notification = new Notification(ApplicationBundle.message("reformat.changed.text.file.too.big.notification.groupId"),
+                                                   ApplicationBundle.message("reformat.changed.text.file.too.big.notification.title"),
+                                                   ApplicationBundle.message("reformat.changed.text.file.too.big.notification.text", file.getName()),
+                                                   NotificationType.INFORMATION);
+      notification.notify(file.getProject());
+    }
+  }
+
+  @Nullable
+  public LayoutCodeInfoCollector getInfoCollector() {
+    return myInfoCollector;
+  }
 }
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/FileInEditorProcessor.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/FileInEditorProcessor.java
new file mode 100644 (file)
index 0000000..75f4c1f
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * 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.codeInsight.actions;
+
+import com.intellij.codeInsight.hint.HintManager;
+import com.intellij.codeInsight.hint.HintManagerImpl;
+import com.intellij.codeInsight.hint.HintUtil;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.keymap.KeymapUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiFile;
+import com.intellij.ui.ColorUtil;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.LightweightHint;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+import static com.intellij.codeInsight.actions.TextRangeType.*;
+
+class FileInEditorProcessor {
+  private static final Logger LOG = Logger.getInstance(FileInEditorProcessor.class);
+
+  private final Editor myEditor;
+
+  private boolean myNoChangesDetected = false;
+  private final boolean myProcessChangesTextOnly;
+
+  private final boolean myShouldOptimizeImports;
+  private final boolean myShouldRearrangeCode;
+  private final boolean myProcessSelectedText;
+
+  private final Project myProject;
+
+  private final PsiFile myFile;
+  private AbstractLayoutCodeProcessor myProcessor;
+
+  public FileInEditorProcessor(PsiFile file,
+                               Editor editor,
+                               LayoutCodeOptions runOptions)
+  {
+    myFile = file;
+    myProject = file.getProject();
+    myEditor = editor;
+
+    myShouldOptimizeImports = runOptions.isOptimizeImports();
+    myShouldRearrangeCode = runOptions.isRearrangeCode();
+    myProcessSelectedText = myEditor != null && runOptions.getTextRangeType() == SELECTED_TEXT;
+    myProcessChangesTextOnly = runOptions.getTextRangeType() == VCS_CHANGED_TEXT;
+  }
+
+  public void processCode() {
+    if (myShouldOptimizeImports) {
+      myProcessor = new OptimizeImportsProcessor(myProject, myFile);
+    }
+
+    if (myProcessChangesTextOnly && !FormatChangedTextUtil.hasChanges(myFile)) {
+      myNoChangesDetected = true;
+    }
+
+    myProcessor = mixWithReformatProcessor(myProcessor);
+    if (myShouldRearrangeCode) {
+      myProcessor = mixWithRearrangeProcessor(myProcessor);
+    }
+
+    if (shouldNotify()) {
+      myProcessor.setCollectInfo(true);
+      myProcessor.setPostRunnable(new Runnable() {
+        @Override
+        public void run() {
+          String message = prepareMessage();
+          if (!myEditor.isDisposed() && myEditor.getComponent().isShowing()) {
+            showHint(myEditor, message);
+          }
+        }
+      });
+    }
+
+    myProcessor.run();
+  }
+
+  private AbstractLayoutCodeProcessor mixWithRearrangeProcessor(@NotNull AbstractLayoutCodeProcessor processor) {
+    if (myProcessSelectedText) {
+      processor = new RearrangeCodeProcessor(processor, myEditor.getSelectionModel());
+    }
+    else {
+      processor = new RearrangeCodeProcessor(processor);
+    }
+    return processor;
+  }
+
+  @NotNull
+  private AbstractLayoutCodeProcessor mixWithReformatProcessor(@Nullable AbstractLayoutCodeProcessor processor) {
+    if (processor != null) {
+      if (myProcessSelectedText) {
+        processor = new ReformatCodeProcessor(processor, myEditor.getSelectionModel());
+      }
+      else {
+        processor = new ReformatCodeProcessor(processor, myProcessChangesTextOnly);
+      }
+    }
+    else {
+      if (myProcessSelectedText) {
+        processor = new ReformatCodeProcessor(myFile, myEditor.getSelectionModel());
+      }
+      else {
+        processor = new ReformatCodeProcessor(myFile, myProcessChangesTextOnly);
+      }
+    }
+    return processor;
+  }
+
+  @NotNull
+  private String prepareMessage() {
+    StringBuilder builder = new StringBuilder("<html>");
+    LayoutCodeInfoCollector notifications = myProcessor.getInfoCollector();
+    LOG.assertTrue(notifications != null);
+
+    if (notifications.isEmpty() && !myNoChangesDetected) {
+      if (myProcessChangesTextOnly) {
+        builder.append("No lines changed: changes since last revision are already properly formatted").append("<br>");
+      }
+      else {
+        builder.append("No lines changed: code is already properly formatted").append("<br>");
+      }
+    }
+    else {
+      if (notifications.hasReformatOrRearrangeNotification()) {
+        String reformatInfo = notifications.getReformatCodeNotification();
+        String rearrangeInfo = notifications.getRearrangeCodeNotification();
+
+        builder.append(joinWithCommaAndCapitalize(reformatInfo, rearrangeInfo));
+
+        if (myProcessChangesTextOnly) {
+          builder.append(" in changes since last revision");
+        }
+
+        builder.append("<br>");
+      }
+      else if (myNoChangesDetected) {
+        builder.append("No lines changed: no changes since last revision").append("<br>");
+      }
+
+      String optimizeImportsNotification = notifications.getOptimizeImportsNotification();
+      if (optimizeImportsNotification != null) {
+        builder.append(StringUtil.capitalize(optimizeImportsNotification)).append("<br>");
+      }
+    }
+
+    String shortcutText = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("ShowReformatFileDialog"));
+    String color = ColorUtil.toHex(JBColor.gray);
+
+    builder.append("<span style='color:#").append(color).append("'>")
+           .append("Show reformat dialog: ").append(shortcutText).append("</span>")
+           .append("</html>");
+
+    return builder.toString();
+  }
+
+  @NotNull
+  private String joinWithCommaAndCapitalize(String reformatNotification, String rearrangeNotification) {
+    String firstNotificationLine = reformatNotification != null ? reformatNotification : rearrangeNotification;
+    if (reformatNotification != null && rearrangeNotification != null) {
+      firstNotificationLine += ", " + rearrangeNotification;
+    }
+    firstNotificationLine = StringUtil.capitalize(firstNotificationLine);
+    return firstNotificationLine;
+  }
+
+  private static void showHint(@NotNull Editor editor, @NotNull String info) {
+    JComponent component = HintUtil.createInformationLabel(info);
+    LightweightHint hint = new LightweightHint(component);
+    HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, HintManager.UNDER,
+                                                     HintManager.HIDE_BY_ANY_KEY |
+                                                     HintManager.HIDE_BY_TEXT_CHANGE |
+                                                     HintManager.HIDE_BY_SCROLLING,
+                                                     0, false);
+  }
+
+  private boolean shouldNotify() {
+    Application application = ApplicationManager.getApplication();
+    if (application.isUnitTestMode() || application.isHeadlessEnvironment()) {
+      return false;
+    }
+    return myEditor != null && !myProcessSelectedText;
+  }
+}
index 03691d0749e05c971ee0980557e0d95dd58d8152..0fe2e90f25c7bb4a4a0df485e8d6c182888797b4 100644 (file)
@@ -54,9 +54,10 @@ import org.jetbrains.annotations.Nullable;
 import java.util.*;
 
 public class FormatChangedTextUtil {
-  public static final Key<String> TEST_REVISION_CONTENT = Key.create("test.revision.content");
+  public static final Key<CharSequence> TEST_REVISION_CONTENT = Key.create("test.revision.content");
   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.actions.FormatChangedTextUtil");
 
+
   private FormatChangedTextUtil() {
   }
 
@@ -71,26 +72,9 @@ public class FormatChangedTextUtil {
     final VirtualFile virtualFile = file.getVirtualFile();
     if (virtualFile != null) {
       final Change change = ChangeListManager.getInstance(project).getChange(virtualFile);
-      if (change != null && change.getType() == Change.Type.NEW) {
-        return true;
-      }
-    }
-
-    final LineStatusTrackerManagerI manager = LineStatusTrackerManager.getInstance(project);
-    if (manager == null) {
-      return false;
+      return change != null;
     }
-    
-    final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
-    if (document == null) {
-      return false;
-    }
-    final LineStatusTracker lineStatusTracker = manager.getLineStatusTracker(document);
-    if (lineStatusTracker == null) {
-      return false;
-    }
-    final List<Range> ranges = lineStatusTracker.getRanges();
-    return !ranges.isEmpty();
+    return false;
   }
 
   /**
@@ -249,15 +233,18 @@ public class FormatChangedTextUtil {
 
   @NotNull
   public static List<TextRange> getChangedTextRanges(@NotNull Project project, @NotNull PsiFile file) throws FilesTooBigForDiffException {
-    List<TextRange> cachedChangedLines = getCachedChangedLines(project, file);
+    Document document = PsiDocumentManager.getInstance(project).getDocument(file);
+    if (document == null) return ContainerUtil.emptyList();
+
+    List<TextRange> cachedChangedLines = getCachedChangedLines(project, document);
     if (cachedChangedLines != null) {
       return cachedChangedLines;
     }
 
     if (ApplicationManager.getApplication().isUnitTestMode()) {
-      String testContent = file.getUserData(TEST_REVISION_CONTENT);
+      CharSequence testContent = file.getUserData(TEST_REVISION_CONTENT);
       if (testContent != null) {
-        return calculateChangedTextRanges(file.getProject(), file, testContent);
+        return calculateChangedTextRanges(document, testContent);
       }
     }
 
@@ -270,17 +257,12 @@ public class FormatChangedTextUtil {
     }
 
     String contentFromVcs = getRevisionedContentFrom(change);
-    return contentFromVcs != null ? calculateChangedTextRanges(project, file, contentFromVcs)
+    return contentFromVcs != null ? calculateChangedTextRanges(document, contentFromVcs)
                                   : ContainerUtil.<TextRange>emptyList();
   }
 
   @Nullable
-  private static List<TextRange> getCachedChangedLines(@NotNull Project project, @NotNull PsiFile file) {
-    Document document = PsiDocumentManager.getInstance(project).getDocument(file);
-    if (document == null) {
-      return ContainerUtil.emptyList();
-    }
-
+  private static List<TextRange> getCachedChangedLines(@NotNull Project project, @NotNull Document document) {
     LineStatusTracker tracker = LineStatusTrackerManager.getInstance(project).getLineStatusTracker(document);
     if (tracker != null) {
       List<Range> ranges = tracker.getRanges();
@@ -307,19 +289,49 @@ public class FormatChangedTextUtil {
   }
 
   @NotNull
-  private static List<TextRange> calculateChangedTextRanges(@NotNull Project project, 
-                                                            @NotNull PsiFile file, 
-                                                            @NotNull String contentFromVcs) throws FilesTooBigForDiffException 
+  protected static List<TextRange> calculateChangedTextRanges(@NotNull Document document,
+                                                              @NotNull CharSequence contentFromVcs) throws FilesTooBigForDiffException
+  {
+    return getChangedTextRanges(document, getRanges(document, contentFromVcs));
+  }
+
+  @NotNull
+  private static List<Range> getRanges(@NotNull Document document,
+                                       @NotNull CharSequence contentFromVcs) throws FilesTooBigForDiffException
   {
     Document documentFromVcs = ((EditorFactoryImpl)EditorFactory.getInstance()).createDocument(contentFromVcs, true, false);
-    Document document = PsiDocumentManager.getInstance(project).getDocument(file);
+    return new RangesBuilder(document, documentFromVcs).getRanges();
+  }
 
-    if (document == null) {
-      return ContainerUtil.emptyList();
+  protected static int calculateChangedLinesNumber(@NotNull Document document, @NotNull CharSequence contentFromVcs) {
+    try {
+      List<Range> changedRanges = getRanges(document, contentFromVcs);
+      int linesChanges = 0;
+      for (Range range : changedRanges) {
+        linesChanges += countLines(range);
+      }
+      return linesChanges;
+    } catch (FilesTooBigForDiffException e) {
+      LOG.info("File too big, can not calculate changed lines number");
+      return -1;
+    }
+  }
+
+  private static int countLines(Range range) {
+    byte rangeType = range.getType();
+    if (rangeType == Range.MODIFIED) {
+      int currentChangedLines = range.getLine2() - range.getLine1();
+      int revisionLinesChanged = range.getVcsLine2() - range.getVcsLine1();
+      return Math.max(currentChangedLines, revisionLinesChanged);
+    }
+    else if (rangeType == Range.DELETED) {
+      return range.getVcsLine2() - range.getVcsLine1();
+    }
+    else if (rangeType == Range.INSERTED) {
+      return range.getLine2() - range.getLine1();
     }
 
-    List<Range> changedRanges = new RangesBuilder(document, documentFromVcs).getRanges();
-    return getChangedTextRanges(document, changedRanges);
+    return 0;
   }
 
   @NotNull
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/LastRunReformatCodeOptionsProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/LastRunReformatCodeOptionsProvider.java
new file mode 100644 (file)
index 0000000..9ba882f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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.codeInsight.actions;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.lang.Language;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+
+public class LastRunReformatCodeOptionsProvider {
+
+  private static final String OPTIMIZE_IMPORTS_KEY     = "LayoutCode.optimizeImports";
+  private static final String REARRANGE_ENTRIES_KEY    = "LayoutCode.rearrangeEntries";
+  private static final String PROCESS_CHANGED_TEXT_KEY = "LayoutCode.processChangedText";
+
+  private final PropertiesComponent myPropertiesComponent;
+
+  public LastRunReformatCodeOptionsProvider(@NotNull PropertiesComponent propertiesComponent) {
+    myPropertiesComponent = propertiesComponent;
+  }
+
+  public ReformatCodeRunOptions getLastRunOptions(@NotNull PsiFile file) {
+    Language language = file.getLanguage();
+
+    ReformatCodeRunOptions settings = new ReformatCodeRunOptions(getLastTextRangeType());
+    settings.setOptimizeImports(getLastOptimizeImports());
+    settings.setRearrangeCode(isRearrangeCode(language));
+
+    return settings;
+  }
+
+  public void saveRearrangeState(@NotNull Language language, boolean value) {
+    String key = getRearrangeCodeKeyFor(language);
+    myPropertiesComponent.setValue(key, Boolean.toString(value));
+  }
+
+  public void saveOptimizeImportsState(boolean value) {
+    String optimizeImports = Boolean.toString(value);
+    myPropertiesComponent.setValue(OPTIMIZE_IMPORTS_KEY, optimizeImports);
+  }
+
+  public boolean getLastOptimizeImports() {
+    return myPropertiesComponent.getBoolean(OPTIMIZE_IMPORTS_KEY, false);
+  }
+
+  public TextRangeType getLastTextRangeType() {
+    return myPropertiesComponent.getBoolean(PROCESS_CHANGED_TEXT_KEY, false) ? TextRangeType.VCS_CHANGED_TEXT : TextRangeType.WHOLE_FILE;
+  }
+
+  public void saveProcessVcsChangedTextState(boolean value) {
+    String processOnlyVcsChangedText = Boolean.toString(value);
+    myPropertiesComponent.setValue(PROCESS_CHANGED_TEXT_KEY, processOnlyVcsChangedText);
+  }
+
+  public void saveRearrangeCodeState(boolean value) {
+    myPropertiesComponent.setValue(REARRANGE_ENTRIES_KEY, Boolean.toString(value));
+  }
+
+  public boolean getLastRearrangeCode() {
+    return myPropertiesComponent.getBoolean(REARRANGE_ENTRIES_KEY, false);
+  }
+
+  public boolean isRearrangeCode(@NotNull Language language) {
+    String key = getRearrangeCodeKeyFor(language);
+    return myPropertiesComponent.getBoolean(key, false);
+  }
+
+  private static String getRearrangeCodeKeyFor(@NotNull Language language) {
+    return REARRANGE_ENTRIES_KEY + language.getDisplayName();
+  }
+
+}
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeDialog.form b/platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeDialog.form
new file mode 100644 (file)
index 0000000..b6e07f4
--- /dev/null
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.codeInsight.actions.LayoutCodeDialog">
+  <grid id="27dc6" binding="myButtonsPanel" layout-manager="GridLayoutManager" row-count="3" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="472" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <vspacer id="52898">
+        <constraints>
+          <grid row="2" column="3" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+        </constraints>
+      </vspacer>
+      <component id="8ca39" class="com.intellij.openapi.ui.Splitter">
+        <constraints>
+          <grid row="1" column="4" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <grid id="91e0a" binding="myActionsPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="9d93" class="javax.swing.JCheckBox" binding="myOptimizeImportsCb" default-binding="true">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <margin top="0" left="0" bottom="0" right="0"/>
+              <text value="&amp;Optimize imports"/>
+            </properties>
+          </component>
+          <vspacer id="2d364">
+            <constraints>
+              <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </vspacer>
+          <component id="a291" class="javax.swing.JCheckBox" binding="myRearrangeCodeCb" default-binding="true">
+            <constraints>
+              <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <margin top="0" left="0" bottom="0" right="0"/>
+              <text value="Rearra&amp;nge code"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="b1a5c" binding="myScopePanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="cea7f" class="javax.swing.JRadioButton" binding="myOnlyVCSChangedTextRb" default-binding="true">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <margin top="0" left="0" bottom="0" right="0"/>
+              <text value="Only &amp;VCS changed text"/>
+            </properties>
+          </component>
+          <hspacer id="6a094">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </hspacer>
+          <vspacer id="467fa">
+            <constraints>
+              <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </vspacer>
+          <component id="27301" class="javax.swing.JRadioButton" binding="mySelectedTextRadioButton" default-binding="true">
+            <constraints>
+              <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <margin top="0" left="0" bottom="0" right="0"/>
+              <text value="&amp;Selected text"/>
+            </properties>
+          </component>
+          <component id="2eea6" class="javax.swing.JRadioButton" binding="myWholeFileRadioButton" default-binding="true">
+            <constraints>
+              <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <margin top="0" left="0" bottom="0" right="0"/>
+              <text value="&amp;Whole file"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <component id="38fdd" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Scope:"/>
+        </properties>
+      </component>
+      <component id="1fda7" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Optional:"/>
+        </properties>
+      </component>
+      <hspacer id="b6b1d">
+        <constraints>
+          <grid row="1" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+        </constraints>
+      </hspacer>
+    </children>
+  </grid>
+  <buttonGroups>
+    <group name="myGroup1">
+      <member id="e5942"/>
+      <member id="4a767"/>
+      <member id="2b374"/>
+      <member id="27301"/>
+      <member id="2eea6"/>
+      <member id="cea7f"/>
+    </group>
+  </buttonGroups>
+</form>
index 716d2f88484499fa3d2c21da773b1a58a1497209..6dc2971b800cdb0fc58226e805cc58e986803ac6 100644 (file)
 
 package com.intellij.codeInsight.actions;
 
-import com.intellij.CommonBundle;
 import com.intellij.codeInsight.CodeInsightBundle;
 import com.intellij.ide.util.PropertiesComponent;
 import com.intellij.lang.LanguageImportStatements;
-import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
 import com.intellij.openapi.help.HelpManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.DialogWrapper;
-import com.intellij.psi.PsiDirectory;
 import com.intellij.psi.PsiFile;
-import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
-import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
 import com.intellij.psi.codeStyle.arrangement.Rearranger;
-import com.intellij.psi.search.SearchScope;
+import com.intellij.testFramework.LightVirtualFile;
+import com.intellij.vcsUtil.VcsUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
-import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 
-public class LayoutCodeDialog extends DialogWrapper implements LayoutCodeOptions {
+public class LayoutCodeDialog extends DialogWrapper {
   @NotNull  private final Project myProject;
-  @Nullable private final PsiFile myFile;
-  @Nullable private final PsiDirectory myDirectory;
-  private final Boolean myTextSelected;
-
-  private JRadioButton myRbFile;
-  private JRadioButton myRbSelectedText;
-  private JRadioButton myRbDirectory;
-  private JCheckBox    myCbIncludeSubdirs;
-  private JCheckBox    myCbOptimizeImports;
-  private JCheckBox    myCbArrangeEntries;
-  private JCheckBox    myCbOnlyVcsChangedRegions;
-  private JCheckBox    myDoNotAskMeCheckBox;
+  @NotNull private final PsiFile myFile;
+
+  private final boolean myTextSelected;
 
   private final String myHelpId;
-  @Nullable private CommonCodeStyleSettings myCommonSettings;
-  private boolean myRearrangeAlwaysEnabled;
+  private final LastRunReformatCodeOptionsProvider myLastRunOptions;
+
+  private JPanel myButtonsPanel;
 
-  private final boolean myOptimizeImportProcessorsForFileLanguageExists;
-  private final boolean myRearrangerProcessorsForFileLanguageExists;
-  private final boolean myFileHasChanges;
+  private JCheckBox myOptimizeImportsCb;
+  private JCheckBox myRearrangeCodeCb;
 
-  private boolean myOptimizeImportsSelected;
-  private boolean myFormatOnlyVCSChangedRegionsSelected;
-  private boolean myDoNotShowDialogSelected;
-  private boolean myRearrangeEntriesSelected;
+  private JRadioButton myOnlyVCSChangedTextRb;
+  private JRadioButton mySelectedTextRadioButton;
+  private JRadioButton myWholeFileRadioButton;
 
+  private JPanel myActionsPanel;
+  private JPanel myScopePanel;
 
-  public  LayoutCodeDialog(@NotNull Project project,
-                          @NotNull String title,
-                          @Nullable PsiFile file,
-                          @Nullable PsiDirectory directory,
-                          Boolean isTextSelected,
+  private LayoutCodeOptions myRunOptions;
+
+  public LayoutCodeDialog(@NotNull Project project,
+                          @NotNull PsiFile file,
+                          boolean textSelected,
                           final String helpId) {
     super(project, true);
     myFile = file;
     myProject = project;
-    myDirectory = directory;
-    myTextSelected = isTextSelected;
-
-    myOptimizeImportProcessorsForFileLanguageExists = myFile != null && !LanguageImportStatements.INSTANCE.forFile(myFile).isEmpty();
-    myRearrangerProcessorsForFileLanguageExists = myFile != null && Rearranger.EXTENSION.forLanguage(myFile.getLanguage()) != null;
-    myFileHasChanges = myFile != null && FormatChangedTextUtil.hasChanges(myFile);
+    myTextSelected = textSelected;
+    myHelpId = helpId;
 
-    if (myFile != null) myCommonSettings = CodeStyleSettingsManager.getSettings(myProject).getCommonSettings(myFile.getLanguage());
-    myRearrangeAlwaysEnabled = myCommonSettings != null
-                               && myCommonSettings.isForceArrangeMenuAvailable()
-                               && myCommonSettings.FORCE_REARRANGE_MODE == CommonCodeStyleSettings.REARRANGE_ALWAYS;
+    myLastRunOptions = new LastRunReformatCodeOptionsProvider(PropertiesComponent.getInstance());
+    myRunOptions = createOptionsBundledOnDialog();
 
     setOKButtonText(CodeInsightBundle.message("reformat.code.accept.button.text"));
-    setTitle(title);
+    setTitle("Reformat File: " + file.getName());
+
     init();
-    myHelpId = helpId;
   }
 
-  @Override
   protected void init() {
     super.init();
 
-    loadCbsStates();
-    setUpInitialSelection();
+    setUpActions();
+    setUpTextRangeMode();
+  }
 
-    myRbFile.addActionListener(new ActionListener() {
-      @Override
-      public void actionPerformed(ActionEvent e) {
-        saveEnabledCbsSelectedState();
-        setUpCbsStateForFileFormatting();
-      }
-    });
 
-    myRbDirectory.addActionListener(new ActionListener() {
-      @Override
-      public void actionPerformed(ActionEvent e) {
-        saveEnabledCbsSelectedState();
-        setUpCbsStatesForDirectoryFormatting();
-      }
-    });
+  private void setUpTextRangeMode() {
+    mySelectedTextRadioButton.setEnabled(myTextSelected);
+    if (!myTextSelected) {
+      mySelectedTextRadioButton.setToolTipText("No text selected in editor");
+    }
 
-    myRbSelectedText.addActionListener(new ActionListener() {
-      @Override
-      public void actionPerformed(ActionEvent e) {
-        saveEnabledCbsSelectedState();
-        setUpCbsStatesForSelectedTextFormatting();
+    final boolean fileHasChanges = FormatChangedTextUtil.hasChanges(myFile);
+    if (myFile.getVirtualFile() instanceof LightVirtualFile) {
+      myOnlyVCSChangedTextRb.setVisible(false);
+    }
+    else {
+      myOnlyVCSChangedTextRb.setEnabled(fileHasChanges);
+      if (!fileHasChanges) {
+        String hint = getChangesNotAvailableHint();
+        if (hint != null) myOnlyVCSChangedTextRb.setToolTipText(hint);
       }
-    });
-  }
+    }
 
-  private void setUpInitialSelection() {
-    if (myTextSelected == Boolean.TRUE) {
-      myRbSelectedText.setSelected(true);
-      setUpCbsStatesForSelectedTextFormatting();
+    myWholeFileRadioButton.setEnabled(true);
+
+    if (myTextSelected) {
+      mySelectedTextRadioButton.setSelected(true);
     }
     else {
-      if (myFile != null) {
-        myRbFile.setSelected(true);
-        setUpCbsStateForFileFormatting();
+      boolean lastRunProcessedChangedText = myLastRunOptions.getLastTextRangeType() == TextRangeType.VCS_CHANGED_TEXT;
+      if (lastRunProcessedChangedText && fileHasChanges) {
+        myOnlyVCSChangedTextRb.setSelected(true);
       }
       else {
-        myRbDirectory.setSelected(true);
-        setUpCbsStatesForDirectoryFormatting();
+        myWholeFileRadioButton.setSelected(true);
       }
     }
-    myCbIncludeSubdirs.setSelected(true);
-  }
-
-  private void loadCbsStates() {
-    myOptimizeImportsSelected = PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.OPTIMIZE_IMPORTS_KEY, false);
-    myRearrangeEntriesSelected = myRearrangeAlwaysEnabled || ReformatCodeAction.getLastSavedRearrangeCbState(myProject, myFile);
-    myFormatOnlyVCSChangedRegionsSelected = PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, false);
   }
 
-  private void saveEnabledCbsSelectedState() {
-    if (myCbArrangeEntries.isEnabled()) {
-      myRearrangeEntriesSelected = myCbArrangeEntries.isSelected();
-    }
-    if (myCbOptimizeImports.isEnabled()) {
-      myOptimizeImportsSelected = myCbOptimizeImports.isSelected();
-    }
-    if (myCbOnlyVcsChangedRegions.isEnabled()) {
-      myFormatOnlyVCSChangedRegionsSelected = myCbOnlyVcsChangedRegions.isSelected();
-    }
-    if (myDoNotAskMeCheckBox.isEnabled()) {
-      myDoNotShowDialogSelected = myDoNotAskMeCheckBox.isSelected();
+  private void setUpActions() {
+    boolean canOptimizeImports = !LanguageImportStatements.INSTANCE.forFile(myFile).isEmpty();
+    myOptimizeImportsCb.setVisible(canOptimizeImports);
+    if (canOptimizeImports) {
+      myOptimizeImportsCb.setSelected(myLastRunOptions.getLastOptimizeImports());
     }
-  }
-
-  private void setUpCbsStateForFileFormatting() {
-    myCbOptimizeImports.setEnabled(myOptimizeImportProcessorsForFileLanguageExists);
-    myCbOptimizeImports.setSelected(myOptimizeImportProcessorsForFileLanguageExists && myOptimizeImportsSelected);
-
-    myCbArrangeEntries.setEnabled(myRearrangerProcessorsForFileLanguageExists);
-    myCbArrangeEntries.setSelected(myRearrangerProcessorsForFileLanguageExists && myRearrangeEntriesSelected);
-
-    myCbOnlyVcsChangedRegions.setEnabled(myFileHasChanges);
-    myCbOnlyVcsChangedRegions.setSelected(myFileHasChanges && myFormatOnlyVCSChangedRegionsSelected);
-
-    myDoNotAskMeCheckBox.setEnabled(true);
-    myDoNotAskMeCheckBox.setSelected(myDoNotShowDialogSelected);
-
-    myCbIncludeSubdirs.setEnabled(false);
-  }
-
-  private void setUpCbsStatesForDirectoryFormatting() {
-    myCbOptimizeImports.setEnabled(true);
-    myCbOptimizeImports.setSelected(myOptimizeImportsSelected);
-
-    myCbArrangeEntries.setEnabled(true);
-    myCbArrangeEntries.setSelected(myRearrangeEntriesSelected);
-
-    myCbOnlyVcsChangedRegions.setEnabled(myDirectory != null && FormatChangedTextUtil.hasChanges(myDirectory));
-    myCbOnlyVcsChangedRegions.setSelected(false);
-
-    myDoNotAskMeCheckBox.setEnabled(false);
-    myDoNotAskMeCheckBox.setSelected(false);
-
-    myCbIncludeSubdirs.setEnabled(true);
-  }
 
-  private void setUpCbsStatesForSelectedTextFormatting() {
-    myCbOptimizeImports.setEnabled(false);
-    myCbOptimizeImports.setSelected(false);
-
-    myCbArrangeEntries.setEnabled(true);
-    myCbArrangeEntries.setSelected(myRearrangeEntriesSelected);
-
-    myCbOnlyVcsChangedRegions.setEnabled(false);
-    myCbOnlyVcsChangedRegions.setSelected(false);
-
-    myDoNotAskMeCheckBox.setEnabled(true);
-    myDoNotAskMeCheckBox.setSelected(myDoNotShowDialogSelected);
-
-    myCbIncludeSubdirs.setEnabled(false);
+    boolean canRearrangeCode = Rearranger.EXTENSION.forLanguage(myFile.getLanguage()) != null;
+    myRearrangeCodeCb.setVisible(canRearrangeCode);
+    if (canRearrangeCode) {
+      myRearrangeCodeCb.setSelected(myLastRunOptions.isRearrangeCode(myFile.getLanguage()));
+    }
   }
 
-  @Override
-  protected JComponent createCenterPanel() {
-    JPanel panel = new JPanel(new GridBagLayout());
-    panel.setBorder(BorderFactory.createEmptyBorder(4, 8, 8, 0));
-    GridBagConstraints gbConstraints = new GridBagConstraints();
-    gbConstraints.gridy = 0;
-    gbConstraints.gridx = 0;
-    gbConstraints.gridwidth = 3;
-    gbConstraints.gridheight = 1;
-    gbConstraints.weightx = 1;
-
-    gbConstraints.fill = GridBagConstraints.BOTH;
-    gbConstraints.insets = new Insets(0, 0, 0, 0);
-
-    myRbFile = new JRadioButton(CodeInsightBundle.message("process.scope.file",
-                                                          (myFile != null ? "'" + myFile.getVirtualFile().getPresentableUrl() + "'" : "")));
-    panel.add(myRbFile, gbConstraints);
-
-    myRbSelectedText = new JRadioButton(CodeInsightBundle.message("reformat.option.selected.text"));
-    if (myTextSelected != null) {
-      gbConstraints.gridy++;
-      gbConstraints.insets = new Insets(0, 0, 0, 0);
-      panel.add(myRbSelectedText, gbConstraints);
+  @Nullable
+  private String getChangesNotAvailableHint() {
+    if (!VcsUtil.isFileUnderVcs(myProject, VcsUtil.getFilePath(myFile.getVirtualFile()))) {
+      return "File not under VCS root";
     }
-
-    myRbDirectory = new JRadioButton();
-    myCbIncludeSubdirs = new JCheckBox(CodeInsightBundle.message("reformat.option.include.subdirectories"));
-    if (myDirectory != null) {
-      myRbDirectory.setText(CodeInsightBundle.message("reformat.option.all.files.in.directory",
-                                                      myDirectory.getVirtualFile().getPresentableUrl()));
-      gbConstraints.gridy++;
-      gbConstraints.insets = new Insets(0, 0, 0, 0);
-      panel.add(myRbDirectory, gbConstraints);
-
-      if (myDirectory.getSubdirectories().length > 0) {
-        gbConstraints.gridy++;
-        gbConstraints.insets = new Insets(0, 20, 0, 0);
-        panel.add(myCbIncludeSubdirs, gbConstraints);
-      }
+    else if (!FormatChangedTextUtil.hasChanges(myFile)) {
+      return "File was not changed since last revision";
     }
+    return null;
+  }
 
-    myCbOptimizeImports = new JCheckBox(CodeInsightBundle.message("reformat.option.optimize.imports"));
-    if (myTextSelected != null && LanguageImportStatements.INSTANCE.hasAnyExtensions()) {
-      gbConstraints.gridy++;
-      gbConstraints.insets = new Insets(0, 0, 0, 0);
-      panel.add(myCbOptimizeImports, gbConstraints);
+  private void saveCurrentConfiguration() {
+    if (myOptimizeImportsCb.isEnabled()) {
+      myLastRunOptions.saveOptimizeImportsState(myRunOptions.isOptimizeImports());
     }
-
-    myCbArrangeEntries = new JCheckBox(CodeInsightBundle.message("reformat.option.rearrange.entries"));
-    if (myDirectory != null || myFile != null && Rearranger.EXTENSION.forLanguage(myFile.getLanguage()) != null)
-    {
-      gbConstraints.gridy++;
-      gbConstraints.insets = new Insets(0, 0, 0, 0);
-      panel.add(myCbArrangeEntries, gbConstraints);
+    if (myRearrangeCodeCb.isEnabled()) {
+      myLastRunOptions.saveRearrangeState(myFile.getLanguage(), myRunOptions.isRearrangeCode());
     }
 
-    myCbOnlyVcsChangedRegions = new JCheckBox(CodeInsightBundle.message("reformat.option.vcs.changed.region"));
-    gbConstraints.gridy++;
-    panel.add(myCbOnlyVcsChangedRegions, gbConstraints);
+    if (!mySelectedTextRadioButton.isSelected()) {
+      myLastRunOptions.saveProcessVcsChangedTextState(myOnlyVCSChangedTextRb.isSelected());
+    }
+  }
 
-    ButtonGroup buttonGroup = new ButtonGroup();
-    buttonGroup.add(myRbFile);
-    buttonGroup.add(myRbSelectedText);
-    buttonGroup.add(myRbDirectory);
+  @NotNull
+  private LayoutCodeOptions createOptionsBundledOnDialog() {
+    return new LayoutCodeOptions() {
+      @Override
+      public TextRangeType getTextRangeType() {
+        if (myOnlyVCSChangedTextRb.isSelected()) {
+          return TextRangeType.VCS_CHANGED_TEXT;
+        }
+        if (mySelectedTextRadioButton.isSelected()) {
+          return TextRangeType.SELECTED_TEXT;
+        }
+        return TextRangeType.WHOLE_FILE;
+      }
 
-    myRbFile.setEnabled(myFile != null);
-    myRbSelectedText.setEnabled(myTextSelected == Boolean.TRUE);
+      @Override
+      public boolean isRearrangeCode() {
+        return myRearrangeCodeCb.isEnabled() && myRearrangeCodeCb.isSelected();
+      }
 
-    return panel;
+      @Override
+      public boolean isOptimizeImports() {
+        return myOptimizeImportsCb.isEnabled() && myOptimizeImportsCb.isSelected();
+      }
+    };
   }
 
+  @Nullable
   @Override
-  protected JComponent createSouthPanel() {
-    JComponent southPanel = super.createSouthPanel();
-    myDoNotAskMeCheckBox = new JCheckBox(CommonBundle.message("dialog.options.do.not.show"));
-    return DialogWrapper.addDoNotShowCheckBox(southPanel, myDoNotAskMeCheckBox);
+  protected JComponent createCenterPanel() {
+    return myButtonsPanel;
   }
 
   @NotNull
@@ -302,81 +196,14 @@ public class LayoutCodeDialog extends DialogWrapper implements LayoutCodeOptions
     HelpManager.getInstance().invokeHelp(myHelpId);
   }
 
-  @Override
-  public boolean isProcessWholeFile() {
-    return myRbFile.isSelected();
-  }
-
-  @Override
-  public boolean isProcessDirectory() {
-    return myRbDirectory.isSelected();
-  }
-
-  @Override
-  public boolean isIncludeSubdirectories() {
-    return myCbIncludeSubdirs.isSelected();
-  }
-
-  @Override
-  public boolean isOptimizeImports() {
-    return myCbOptimizeImports.isSelected();
-  }
-
-  @Override
-  public boolean isRearrangeEntries() {
-    return myCbArrangeEntries.isSelected();
-  }
-
-  @Nullable
-  @Override
-  public String getFileTypeMask() {
-    return null;
-  }
-
-  @Nullable
-  @Override
-  public SearchScope getSearchScope() {
-    return null;
-  }
-
-  @Override
-  public boolean isProcessOnlyChangedText() {
-    return myCbOnlyVcsChangedRegions.isEnabled() && myCbOnlyVcsChangedRegions.isSelected();
-  }
-
-  public boolean isDoNotAskMe() {
-    if (myDoNotAskMeCheckBox.isEnabled()) {
-      return myDoNotAskMeCheckBox.isSelected();
-    }
-    else {
-      return !EditorSettingsExternalizable.getInstance().getOptions().SHOW_REFORMAT_DIALOG;
-    }
-  }
-
   @Override
   protected void doOKAction() {
     super.doOKAction();
-    persistEnabledCbsStates();
+    saveCurrentConfiguration();
   }
 
-  private void persistEnabledCbsStates() {
-    if (myCbOptimizeImports.isEnabled()) {
-      String optimizeImports = Boolean.toString(myCbOptimizeImports.isSelected());
-      PropertiesComponent.getInstance().setValue(LayoutCodeConstants.OPTIMIZE_IMPORTS_KEY, optimizeImports);
-    }
-    if (myCbOnlyVcsChangedRegions.isEnabled()) {
-      String formatVcsChangedRegions = Boolean.toString(myCbOnlyVcsChangedRegions.isSelected());
-      PropertiesComponent.getInstance().setValue(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, formatVcsChangedRegions);
-    }
-    if (myCbArrangeEntries.isEnabled()) {
-      saveRearrangeCbState(myCbArrangeEntries.isSelected());
-    }
+  public LayoutCodeOptions getRunOptions() {
+    return myRunOptions;
   }
 
-  private void saveRearrangeCbState(boolean isSelected) {
-    if (myFile != null)
-      LayoutCodeSettingsStorage.saveRearrangeEntriesOptionFor(myProject, myFile.getLanguage(), isSelected);
-    else
-      LayoutCodeSettingsStorage.saveRearrangeEntriesOptionFor(myProject, isSelected);
-  }
 }
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeInfoCollector.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeInfoCollector.java
new file mode 100644 (file)
index 0000000..8fc7a60
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.codeInsight.actions;
+
+public class LayoutCodeInfoCollector {
+
+  private String optimizeImportsNotification = null;
+  private String reformatCodeNotification = null;
+  private String rearrangeCodeNotification = null;
+
+  public String getOptimizeImportsNotification() {
+    return optimizeImportsNotification;
+  }
+
+  public void setOptimizeImportsNotification(String optimizeImportsNotification) {
+    this.optimizeImportsNotification = optimizeImportsNotification;
+  }
+
+  public String getReformatCodeNotification() {
+    return reformatCodeNotification;
+  }
+
+  public void setReformatCodeNotification(String reformatCodeNotification) {
+    this.reformatCodeNotification = reformatCodeNotification;
+  }
+
+  public String getRearrangeCodeNotification() {
+    return rearrangeCodeNotification;
+  }
+
+  public void setRearrangeCodeNotification(String rearrangeCodeNotification) {
+    this.rearrangeCodeNotification = rearrangeCodeNotification;
+  }
+
+  public boolean hasReformatOrRearrangeNotification() {
+    return rearrangeCodeNotification != null
+           || reformatCodeNotification != null;
+  }
+
+  public boolean isEmpty() {
+    return optimizeImportsNotification == null
+           && rearrangeCodeNotification == null
+           && reformatCodeNotification == null;
+  }
+}
index 5a05a9ac13abcfac4e97536dda2e072d3de3a54e..2400e3b628af9d9e8c2e66ecfec364cfaa030166 100644 (file)
  */
 package com.intellij.codeInsight.actions;
 
-public interface LayoutCodeOptions extends DirectoryFormattingOptions {
+public interface LayoutCodeOptions extends OptionalReformatActions {
 
-  boolean isProcessWholeFile();
+  TextRangeType getTextRangeType();
 
-  boolean isProcessDirectory();
+}
 
+enum TextRangeType {
+  VCS_CHANGED_TEXT,
+  SELECTED_TEXT,
+  WHOLE_FILE
 }
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeSettingsStorage.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeSettingsStorage.java
deleted file mode 100644 (file)
index 5175cf5..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2000-2013 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.codeInsight.actions;
-
-import com.intellij.ide.util.PropertiesComponent;
-import com.intellij.lang.Language;
-import com.intellij.openapi.project.Project;
-import org.jetbrains.annotations.NotNull;
-
-public class LayoutCodeSettingsStorage {
-
-  private LayoutCodeSettingsStorage() {
-  }
-
-  public static void saveRearrangeEntriesOptionFor(@NotNull Project project, @NotNull Language language, boolean value) {
-    String key = getRearrangeEntriesKeyForLanguage(language);
-    PropertiesComponent.getInstance(project).setValue(key, Boolean.toString(value));
-  }
-
-  public static void saveRearrangeEntriesOptionFor(@NotNull Project project, boolean value) {
-    PropertiesComponent.getInstance(project).setValue(LayoutCodeConstants.REARRANGE_ENTRIES_KEY, Boolean.toString(value));
-  }
-
-  public static boolean getLastSavedRearrangeEntriesCbStateFor(@NotNull Project project) {
-    return PropertiesComponent.getInstance(project).getBoolean(LayoutCodeConstants.REARRANGE_ENTRIES_KEY, false);
-  }
-
-  public static boolean getLastSavedRearrangeEntriesCbStateFor(@NotNull Project project, @NotNull Language language) {
-    String key = getRearrangeEntriesKeyForLanguage(language);
-    return PropertiesComponent.getInstance(project).getBoolean(key, false);
-  }
-
-  private static String getRearrangeEntriesKeyForLanguage(@NotNull Language language) {
-    return LayoutCodeConstants.REARRANGE_ENTRIES_KEY + language.getDisplayName();
-  }
-
-}
index 7f76f1b012a5879164d1e25a0632b1aef734a05a..7690e09339f8a05bdcf87cd5dc3d75ec172b56c7 100644 (file)
@@ -47,6 +47,7 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
   private final Project myProject;
   private final String  myText;
   private final boolean myEnableOnlyVCSChangedTextCb;
+  private final LastRunReformatCodeOptionsProvider myLastRunOptions;
 
   private JLabel myTitle;
   protected JCheckBox myIncludeSubdirsCb;
@@ -75,6 +76,7 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
     myText = text;
     myProject = project;
     myEnableOnlyVCSChangedTextCb = enableOnlyVCSChangedTextCb;
+    myLastRunOptions = new LastRunReformatCodeOptionsProvider(PropertiesComponent.getInstance());
 
     setOKButtonText(CodeInsightBundle.message("reformat.code.accept.button.text"));
     setTitle(title);
@@ -100,11 +102,11 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
   }
 
   private void restoreCbsStates() {
-    myCbOptimizeImports.setSelected(PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.OPTIMIZE_IMPORTS_KEY, false));
-    myCbRearrangeEntries.setSelected(LayoutCodeSettingsStorage.getLastSavedRearrangeEntriesCbStateFor(myProject));
+    myCbOptimizeImports.setSelected(myLastRunOptions.getLastOptimizeImports());
+    myCbRearrangeEntries.setSelected(myLastRunOptions.getLastRearrangeCode());
     myCbOnlyVcsChangedRegions.setEnabled(myEnableOnlyVCSChangedTextCb);
     myCbOnlyVcsChangedRegions.setSelected(
-      myEnableOnlyVCSChangedTextCb && PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, false)
+      myEnableOnlyVCSChangedTextCb && myLastRunOptions.getLastTextRangeType() == TextRangeType.VCS_CHANGED_TEXT
     );
   }
 
@@ -177,7 +179,7 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
   }
 
   @Override
-  public boolean isRearrangeEntries() {
+  public boolean isRearrangeCode() {
     return myCbRearrangeEntries.isSelected();
   }
 
@@ -189,10 +191,10 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
   @Override
   protected void doOKAction() {
     super.doOKAction();
-    PropertiesComponent.getInstance().setValue(LayoutCodeConstants.OPTIMIZE_IMPORTS_KEY, Boolean.toString(isOptimizeImports()));
-    LayoutCodeSettingsStorage.saveRearrangeEntriesOptionFor(myProject, isRearrangeEntries());
+    myLastRunOptions.saveOptimizeImportsState(isOptimizeImports());
+    myLastRunOptions.saveRearrangeCodeState(isRearrangeCode());
     if (myEnableOnlyVCSChangedTextCb) {
-      PropertiesComponent.getInstance().setValue(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, Boolean.toString(myCbOnlyVcsChangedRegions.isSelected()));
+      myLastRunOptions.saveProcessVcsChangedTextState(getTextRangeType() == TextRangeType.VCS_CHANGED_TEXT);
     }
   }
 
@@ -200,10 +202,6 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
     return myCbOptimizeImports.isSelected();
   }
 
-  public boolean isProcessOnlyChangedText() {
-    return myCbOnlyVcsChangedRegions.isEnabled() && myCbOnlyVcsChangedRegions.isSelected();
-  }
-
   @Nullable
   public String getFileTypeMask() {
     if (myEnableFileNameFilterCb.isSelected()) {
@@ -232,4 +230,10 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
     return false;
   }
 
+  @Override
+  public TextRangeType getTextRangeType() {
+    return myCbOnlyVcsChangedRegions.isEnabled() && myCbOnlyVcsChangedRegions.isSelected()
+           ? TextRangeType.VCS_CHANGED_TEXT
+           : TextRangeType.WHOLE_FILE;
+  }
 }
index 0295fee17973cbf1074168915acb0f63a6d7e4c0..3f17fc67a5ec7e8e33e5ff78ae62bed7b8b2313d 100644 (file)
 package com.intellij.codeInsight.actions;
 
 import com.intellij.codeInsight.CodeInsightBundle;
+import com.intellij.ide.util.PropertiesComponent;
 import com.intellij.lang.LanguageImportStatements;
 import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.DialogWrapper;
@@ -72,13 +72,16 @@ public class OptimizeImportsAction extends AnAction {
 
       if (projectContext != null || moduleContext != null) {
         final String text;
+        final boolean hasChanges;
         if (moduleContext != null) {
           text = CodeInsightBundle.message("process.scope.module", moduleContext.getName());
+          hasChanges = FormatChangedTextUtil.hasChanges(moduleContext);
         }
         else {
           text = CodeInsightBundle.message("process.scope.project", projectContext.getPresentableUrl());
+          hasChanges = FormatChangedTextUtil.hasChanges(projectContext);
         }
-        DialogWrapper dialog = new OptimizeOnModuleDialog(project, text);
+        DialogWrapper dialog = new OptimizeImportsDialog(project, text, hasChanges);
         if (!dialog.showAndGet()) {
           return;
         }
@@ -106,29 +109,21 @@ public class OptimizeImportsAction extends AnAction {
       }
     }
 
-    boolean processDirectory;
-    boolean includeSubdirectories;
-
-    if (ApplicationManager.getApplication().isUnitTestMode()) {
-      includeSubdirectories = processDirectory = false;
-    }
-    else if (!EditorSettingsExternalizable.getInstance().getOptions().SHOW_OPIMIZE_IMPORTS_DIALOG && file != null) {
-      includeSubdirectories = processDirectory = false;
-    }
-    else {
-      final LayoutCodeDialog dialog =
-        new LayoutCodeDialog(project, CodeInsightBundle.message("process.optimize.imports"), file, dir, null, HELP_ID);
-      if (!dialog.showAndGet()) {
+    boolean processDirectory = false;
+    boolean processOnlyVcsChangedFiles = false;
+    if (!ApplicationManager.getApplication().isUnitTestMode() && file == null && dir != null) {
+      String message = CodeInsightBundle.message("process.scope.directory", dir.getName());
+      OptimizeImportsDialog dialog = new OptimizeImportsDialog(project, message, FormatChangedTextUtil.hasChanges(dir));
+      dialog.show();
+      if (!dialog.isOK()) {
         return;
       }
-      EditorSettingsExternalizable.getInstance().getOptions().SHOW_OPIMIZE_IMPORTS_DIALOG = !dialog.isDoNotAskMe();
-      ReformatCodeAction.updateShowDialogSetting(dialog, "\"Optimize Imports\" dialog disabled");
-      processDirectory = dialog.isProcessDirectory();
-      includeSubdirectories = dialog.isIncludeSubdirectories();
+      processDirectory = true;
+      processOnlyVcsChangedFiles = dialog.isProcessOnlyVcsChangedFiles();
     }
 
     if (processDirectory){
-      new OptimizeImportsProcessor(project, dir, includeSubdirectories).run();
+      new OptimizeImportsProcessor(project, dir, true, processOnlyVcsChangedFiles).run();
     }
     else{
       new OptimizeImportsProcessor(project, file).run();
@@ -204,21 +199,43 @@ public class OptimizeImportsAction extends AnAction {
     return !LanguageImportStatements.INSTANCE.forFile(file).isEmpty();
   }
 
-  private static class OptimizeOnModuleDialog extends DialogWrapper {
+  private static class OptimizeImportsDialog extends DialogWrapper {
+    private final boolean myContextHasChanges;
+
     private final String myText;
+    private JCheckBox myOnlyVcsCheckBox;
+    private final LastRunReformatCodeOptionsProvider myLastRunOptions;
 
-    OptimizeOnModuleDialog(Project project, String text) {
+    OptimizeImportsDialog(Project project, String text, boolean hasChanges) {
       super(project, false);
       myText = text;
+      myContextHasChanges = hasChanges;
+      myLastRunOptions = new LastRunReformatCodeOptionsProvider(PropertiesComponent.getInstance());
       setOKButtonText(CodeInsightBundle.message("reformat.code.accept.button.text"));
       setTitle(CodeInsightBundle.message("process.optimize.imports"));
       init();
     }
 
+    public boolean isProcessOnlyVcsChangedFiles() {
+      return myOnlyVcsCheckBox.isSelected();
+    }
+
     @Nullable
     @Override
     protected JComponent createCenterPanel() {
-      return new JLabel(myText);
+      JPanel panel = new JPanel();
+      BoxLayout layout = new BoxLayout(panel, BoxLayout.Y_AXIS);
+      panel.setLayout(layout);
+
+      panel.add(new JLabel(myText));
+      myOnlyVcsCheckBox = new JCheckBox(CodeInsightBundle.message("process.scope.changed.files"));
+      boolean lastRunVcsChangedTextEnabled = myLastRunOptions.getLastTextRangeType() == TextRangeType.VCS_CHANGED_TEXT;
+
+      myOnlyVcsCheckBox.setEnabled(myContextHasChanges);
+      myOnlyVcsCheckBox.setSelected(myContextHasChanges && lastRunVcsChangedTextEnabled);
+
+      panel.add(myOnlyVcsCheckBox);
+      return panel;
     }
   }
 }
index 6cdb063c04349d738b5306790dc2af7c2c884f96..1428605281ff9b1308cd08a4a311a752805308a6 100644 (file)
@@ -25,16 +25,21 @@ import com.intellij.openapi.util.EmptyRunnable;
 import com.intellij.psi.PsiDirectory;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.impl.source.codeStyle.CodeStyleManagerImpl;
+import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.FutureTask;
 
+import static com.intellij.codeInsight.actions.OptimizeImportsProcessor.NotificationInfo.*;
+
 public class OptimizeImportsProcessor extends AbstractLayoutCodeProcessor {
   private static final String PROGRESS_TEXT = CodeInsightBundle.message("progress.text.optimizing.imports");
   public static final String COMMAND_NAME = CodeInsightBundle.message("process.optimize.imports");
+  private List<NotificationInfo> myOptimizerNotifications = ContainerUtil.newSmartList();
 
   public OptimizeImportsProcessor(Project project) {
     super(project, COMMAND_NAME, PROGRESS_TEXT, false);
@@ -48,6 +53,10 @@ public class OptimizeImportsProcessor extends AbstractLayoutCodeProcessor {
     super(project, directory, includeSubdirs, PROGRESS_TEXT, COMMAND_NAME, false);
   }
 
+  public OptimizeImportsProcessor(Project project, PsiDirectory directory, boolean includeSubdirs, boolean processOnlyVcsChangedFiles) {
+    super(project, directory, includeSubdirs, PROGRESS_TEXT, COMMAND_NAME, processOnlyVcsChangedFiles);
+  }
+
   public OptimizeImportsProcessor(Project project, PsiFile file) {
     super(project, file, PROGRESS_TEXT, COMMAND_NAME, false);
   }
@@ -77,20 +86,79 @@ public class OptimizeImportsProcessor extends AbstractLayoutCodeProcessor {
         }
       }
     }
-    Runnable runnable = runnables.isEmpty() ? EmptyRunnable.getInstance() : new Runnable() {
+
+    Runnable runnable = !runnables.isEmpty() ? new Runnable() {
       @Override
       public void run() {
         CodeStyleManagerImpl.setSequentialProcessingAllowed(false);
         try {
           for (Runnable runnable : runnables) {
             runnable.run();
+            retrieveAndStoreNotificationInfo(runnable);
           }
+          putNotificationInfoIntoCollector();
         }
         finally {
           CodeStyleManagerImpl.setSequentialProcessingAllowed(true);
         }
       }
-    };
+    } : EmptyRunnable.getInstance();
     return new FutureTask<Boolean>(runnable, true);
   }
+
+  private void retrieveAndStoreNotificationInfo(@NotNull Runnable runnable) {
+    if (runnable instanceof ImportOptimizer.CollectingInfoRunnable) {
+      String optimizerMessage = ((ImportOptimizer.CollectingInfoRunnable)runnable).getUserNotificationInfo();
+      myOptimizerNotifications.add(optimizerMessage != null ? new NotificationInfo(optimizerMessage) : NOTHING_CHANGED_NOTIFICATION);
+    }
+    else if (runnable == EmptyRunnable.getInstance()) {
+      myOptimizerNotifications.add(NOTHING_CHANGED_NOTIFICATION);
+    }
+    else {
+      myOptimizerNotifications.add(SOMETHING_CHANGED_WITHOUT_MESSAGE_NOTIFICATION);
+    }
+  }
+
+  private void putNotificationInfoIntoCollector() {
+    LayoutCodeInfoCollector collector = getInfoCollector();
+    if (collector == null) {
+      return;
+    }
+
+    boolean atLeastOneOptimizerChangedSomething = false;
+    for (NotificationInfo info : myOptimizerNotifications) {
+      atLeastOneOptimizerChangedSomething |= info.isSomethingChanged();
+      if (info.getMessage() != null) {
+        collector.setOptimizeImportsNotification(info.getMessage());
+        return;
+      }
+    }
+
+    collector.setOptimizeImportsNotification(atLeastOneOptimizerChangedSomething ? "imports optimized" : null);
+  }
+
+  static class NotificationInfo {
+    public static final NotificationInfo NOTHING_CHANGED_NOTIFICATION = new NotificationInfo(false, null);
+    public static final NotificationInfo SOMETHING_CHANGED_WITHOUT_MESSAGE_NOTIFICATION = new NotificationInfo(true, null);
+
+    private final boolean mySomethingChanged;
+    private final String myMessage;
+
+    NotificationInfo(@NotNull String message) {
+      this(true, message);
+    }
+
+    public boolean isSomethingChanged() {
+      return mySomethingChanged;
+    }
+
+    public String getMessage() {
+      return myMessage;
+    }
+
+    private NotificationInfo(boolean isSomethingChanged, @Nullable String message) {
+      mySomethingChanged = isSomethingChanged;
+      myMessage = message;
+    }
+  }
 }
similarity index 54%
rename from platform/lang-impl/src/com/intellij/codeInsight/actions/LayoutCodeConstants.java
rename to platform/lang-impl/src/com/intellij/codeInsight/actions/OptionalReformatActions.java
index 84de47d7f4579a2b35f3443a97f90ca826b4313c..e71f5c116ad50ab4ae1c6e9e263a749008b30442 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * 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.
  */
 package com.intellij.codeInsight.actions;
 
-import org.jetbrains.annotations.NonNls;
+public interface OptionalReformatActions {
 
-/**
- * @author Denis Zhdanov
- * @since 12/16/11 6:10 PM
- */
-public class LayoutCodeConstants {
+  boolean isOptimizeImports();
 
-  @NonNls public static final String OPTIMIZE_IMPORTS_KEY     = "LayoutCode.optimizeImports";
-  @NonNls public static final String REARRANGE_ENTRIES_KEY    = "LayoutCode.rearrangeEntries";
-  @NonNls public static final String PROCESS_CHANGED_TEXT_KEY = "LayoutCode.processChangedText";
+  boolean isRearrangeCode();
 
-  private LayoutCodeConstants() {
-  }
 }
index 4867d85abbd7e94f5afa105e24a5446890e9277a..fc264cdb61ccc316db83f9af95438dc536b4bdb2 100644 (file)
  */
 package com.intellij.codeInsight.actions;
 
+import com.intellij.codeInsight.CodeInsightBundle;
 import com.intellij.openapi.command.CommandProcessor;
 import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.SelectionModel;
 import com.intellij.openapi.project.Project;
@@ -25,21 +27,22 @@ import com.intellij.psi.PsiDocumentManager;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.codeStyle.arrangement.Rearranger;
 import com.intellij.psi.codeStyle.arrangement.engine.ArrangementEngine;
-import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.diff.FilesTooBigForDiffException;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
-import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.FutureTask;
 
 public class RearrangeCodeProcessor extends AbstractLayoutCodeProcessor {
 
   public static final String COMMAND_NAME = "Rearrange code";
-  public static final String PROGRESS_TEXT = "Rearranging code...";
+  public static final String PROGRESS_TEXT = CodeInsightBundle.message("process.rearrange.code");
 
-  @Nullable private SelectionModel mySelectionModel;
+  private static final Logger LOG = Logger.getInstance(RearrangeCodeProcessor.class);
+  private SelectionModel mySelectionModel;
 
   public RearrangeCodeProcessor(@NotNull AbstractLayoutCodeProcessor previousProcessor) {
     super(previousProcessor, COMMAND_NAME, PROGRESS_TEXT);
@@ -49,14 +52,16 @@ public class RearrangeCodeProcessor extends AbstractLayoutCodeProcessor {
     super(previousProcessor, COMMAND_NAME, PROGRESS_TEXT);
     mySelectionModel = selectionModel;
   }
-  
-  public RearrangeCodeProcessor(@NotNull Project project,
-                                @NotNull PsiFile file,
-                                @Nullable SelectionModel selectionModel) {
-    super(project, file, PROGRESS_TEXT, COMMAND_NAME, false);
+
+  public RearrangeCodeProcessor(@NotNull PsiFile file, @NotNull SelectionModel selectionModel) {
+    super(file.getProject(), file, PROGRESS_TEXT, COMMAND_NAME, false);
     mySelectionModel = selectionModel;
   }
 
+  public RearrangeCodeProcessor(@NotNull PsiFile file) {
+    super(file.getProject(), file, PROGRESS_TEXT, COMMAND_NAME, false);
+  }
+
   public RearrangeCodeProcessor(@NotNull Project project,
                                 @NotNull PsiFile[] files,
                                 @NotNull String commandName,
@@ -70,70 +75,55 @@ public class RearrangeCodeProcessor extends AbstractLayoutCodeProcessor {
     return new FutureTask<Boolean>(new Callable<Boolean>() {
       @Override
       public Boolean call() throws Exception {
-        Collection<TextRange> ranges = processChangedTextOnly ? FormatChangedTextUtil.getChangedTextRanges(myProject, file)
-                                                              : getRangesToFormat(file);
-
-        RearrangeCommand rearranger = new RearrangeCommand(myProject, file, COMMAND_NAME, ranges);
-        if (rearranger.couldRearrange()) {
-          rearranger.run();
+        try {
+          Collection<TextRange> ranges = getRangesToFormat(file, processChangedTextOnly);
+          Document document = PsiDocumentManager.getInstance(myProject).getDocument(file);
+
+          if (document != null && Rearranger.EXTENSION.forLanguage(file.getLanguage()) != null) {
+            Runnable command = prepareRearrangeCommand(file, ranges);
+            PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(document);
+            try {
+              CommandProcessor.getInstance().executeCommand(myProject, command, COMMAND_NAME, null);
+            }
+            finally {
+              PsiDocumentManager.getInstance(myProject).commitDocument(document);
+            }
+          }
+
+          return true;
+        }
+        catch (FilesTooBigForDiffException e) {
+          handleFileTooBigException(LOG, e, file);
+          return false;
         }
-        return true;
       }
     });
   }
 
-  public Collection<TextRange> getRangesToFormat(@NotNull PsiFile file) {
-    final List<TextRange> ranges = new SmartList<TextRange>();
-    if (mySelectionModel != null && mySelectionModel.hasSelection()) {
-      ranges.add(TextRange.create(mySelectionModel.getSelectionStart(), mySelectionModel.getSelectionEnd()));
-    }
-    else {
-      ranges.add(TextRange.create(0, file.getTextLength()));
-    }
-    return ranges;
-  }
-}
-
-
-class RearrangeCommand {
-  @NotNull private PsiFile myFile;
-  @NotNull private String myCommandName;
-  @NotNull private Project myProject;
-  private Document myDocument;
-  private Runnable myCommand;
-  private final Collection<TextRange> myRanges;
-
-  RearrangeCommand(@NotNull Project project, @NotNull PsiFile file, @NotNull String commandName, @NotNull Collection<TextRange> ranges) {
-    myProject = project;
-    myFile = file;
-    myRanges = ranges;
-    myCommandName = commandName;
-    myDocument = PsiDocumentManager.getInstance(project).getDocument(file);
-  }
-
-  boolean couldRearrange() {
-    return myDocument != null && Rearranger.EXTENSION.forLanguage(myFile.getLanguage()) != null;
-  }
-
-  void run() {
-    assert myDocument != null;
-    prepare();
-    try {
-      CommandProcessor.getInstance().executeCommand(myProject, myCommand, myCommandName, null);
-    }
-    finally {
-      PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
-    }
-  }
-
-  private void prepare() {
+  @NotNull
+  private Runnable prepareRearrangeCommand(@NotNull final PsiFile file, @NotNull final Collection<TextRange> ranges) {
     final ArrangementEngine engine = ServiceManager.getService(myProject, ArrangementEngine.class);
-    myCommand = new Runnable() {
+    return new Runnable() {
       @Override
       public void run() {
-        engine.arrange(myFile, myRanges);
+        engine.arrange(file, ranges);
+        if (getInfoCollector() != null) {
+          String info = engine.getUserNotificationInfo();
+          getInfoCollector().setRearrangeCodeNotification(info);
+        }
       }
     };
-    PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myDocument);
+  }
+
+  public Collection<TextRange> getRangesToFormat(@NotNull PsiFile file, boolean processChangedTextOnly) throws FilesTooBigForDiffException {
+    if (mySelectionModel != null) {
+      return getSelectedRanges(mySelectionModel);
+    }
+
+    if (processChangedTextOnly) {
+      return FormatChangedTextUtil.getChangedTextRanges(myProject, file);
+    }
+
+    return ContainerUtil.newSmartList(file.getTextRange());
   }
 }
index ba3d2d961f00ec67ecd6d6d9e85328292edfb222..bdff57d94240c6ae311da0e4c83d2502bf203929 100644 (file)
 
 package com.intellij.codeInsight.actions;
 
-import com.intellij.application.options.editor.EditorOptions;
 import com.intellij.codeInsight.CodeInsightBundle;
 import com.intellij.find.impl.FindInProjectUtil;
 import com.intellij.formatting.FormattingModelBuilder;
 import com.intellij.ide.util.PropertiesComponent;
 import com.intellij.lang.LanguageFormatting;
-import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationListener;
-import com.intellij.notification.NotificationType;
-import com.intellij.notification.Notifications;
 import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
 import com.intellij.openapi.module.Module;
-import com.intellij.openapi.options.ShowSettingsUtil;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.DumbService;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.vcs.changes.ChangeListManagerImpl;
 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
 import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.wm.IdeFrame;
-import com.intellij.openapi.wm.ex.WindowManagerEx;
 import com.intellij.psi.*;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.psi.search.LocalSearchScope;
@@ -53,8 +43,6 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.annotations.TestOnly;
 
-import javax.swing.*;
-import javax.swing.event.HyperlinkEvent;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.regex.Pattern;
@@ -95,14 +83,14 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
         if (selectedFlags == null)
           return;
 
-        final boolean processOnlyChangedText = selectedFlags.isProcessOnlyChangedText();
+        final boolean processOnlyChangedText = selectedFlags.getTextRangeType() == TextRangeType.VCS_CHANGED_TEXT;
         final boolean shouldOptimizeImports = selectedFlags.isOptimizeImports() && !DumbService.getInstance(project).isDumb();
 
         AbstractLayoutCodeProcessor processor = new ReformatCodeProcessor(project, convertToPsiFiles(files, project), null, processOnlyChangedText);
         if (shouldOptimizeImports) {
           processor = new OptimizeImportsProcessor(processor);
         }
-        if (selectedFlags.isRearrangeEntries()) {
+        if (selectedFlags.isRearrangeCode()) {
           processor = new RearrangeCodeProcessor(processor);
         }
 
@@ -134,15 +122,7 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
       }
     }
 
-    boolean optimizeImports = ReformatFilesDialog.isOptmizeImportsOptionOn();
-    boolean processWholeFile = false;
-    boolean processChangedTextOnly = PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, false);
-    boolean rearrangeEntries = getLastSavedRearrangeCbState(project, file);
-
-    final boolean showDialog = EditorSettingsExternalizable.getInstance().getOptions().SHOW_REFORMAT_DIALOG;
-
-    if (file == null && dir == null) return;
-    if (file == null) {
+    if (file == null && dir != null) {
       DirectoryFormattingOptions options = getDirectoryFormattingOptions(project, dir);
       if (options != null) {
         reformatDirectory(project, dir, options);
@@ -150,57 +130,29 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
       return;
     }
 
-    if (showDialog) {
-      LayoutCodeOptions selectedFlags = getLayoutCodeOptions(project, file, dir, hasSelection);
-      if (selectedFlags == null)
-        return;
+    if (file == null || editor == null) return;
 
-      optimizeImports = selectedFlags.isOptimizeImports();
-      rearrangeEntries = selectedFlags.isRearrangeEntries();
-      processWholeFile = selectedFlags.isProcessWholeFile();
-      processChangedTextOnly = selectedFlags.isProcessOnlyChangedText();
-
-      if (selectedFlags.isProcessDirectory()) {
-        assert dir != null : "File = " + file + ", Element = " + CommonDataKeys.PSI_ELEMENT.getData(dataContext);
-        reformatDirectory(project, dir, selectedFlags);
-        return;
-      }
-    }
-
-    if (!showDialog && processChangedTextOnly && isChangeNotTrackedForFile(project, file)) {
-      processChangedTextOnly = false;
-    }
+    LastRunReformatCodeOptionsProvider provider = new LastRunReformatCodeOptionsProvider(PropertiesComponent.getInstance());
+    ReformatCodeRunOptions currentRunOptions = provider.getLastRunOptions(file);
 
-    final TextRange range;
-    final boolean processSelectedText = !processWholeFile && hasSelection;
-    if (processSelectedText) {
-      range = TextRange.create(editor.getSelectionModel().getSelectionStart(), editor.getSelectionModel().getSelectionEnd());
+    TextRangeType processingScope = currentRunOptions.getTextRangeType();
+    if (hasSelection) {
+      processingScope = TextRangeType.SELECTED_TEXT;
     }
-    else{
-      range = null;
-    }
-
-    AbstractLayoutCodeProcessor processor;
-    if (optimizeImports && range == null) {
-      processor = new OptimizeImportsProcessor(project, file);
-      processor = new ReformatCodeProcessor(processor, processChangedTextOnly);
+    else if (processingScope == TextRangeType.VCS_CHANGED_TEXT) {
+      if (isChangeNotTrackedForFile(project, file)) {
+        processingScope = TextRangeType.WHOLE_FILE;
+      }
     }
     else {
-      processor = new ReformatCodeProcessor(project, file, range, !processSelectedText && processChangedTextOnly);
-    }
-
-    if (rearrangeEntries) {
-      if (processSelectedText && editor != null) {
-        processor = new RearrangeCodeProcessor(processor, editor.getSelectionModel());
-      }
-      else {
-        processor = new RearrangeCodeProcessor(processor);
-      }
+      processingScope = TextRangeType.WHOLE_FILE;
     }
 
-    processor.run();
+    currentRunOptions.setProcessingScope(processingScope);
+    new FileInEditorProcessor(file, editor, currentRunOptions).processCode();
   }
 
+
   private static boolean isChangeNotTrackedForFile(@NotNull Project project, @NotNull PsiFile file) {
     boolean isUnderVcs = VcsUtil.isFileUnderVcs(project, VcsUtil.getFilePath(file.getVirtualFile()));
     if (!isUnderVcs) return true;
@@ -238,10 +190,7 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
                                         @NotNull DirectoryFormattingOptions options)
   {
     AbstractLayoutCodeProcessor processor = new ReformatCodeProcessor(
-      project,
-      dir,
-      options.isIncludeSubdirectories(),
-      options.isProcessOnlyChangedText()
+      project, dir, options.isIncludeSubdirectories(), options.getTextRangeType() == TextRangeType.VCS_CHANGED_TEXT
     );
 
     registerScopeFilter(processor, options.getSearchScope());
@@ -250,7 +199,7 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
     if (options.isOptimizeImports()) {
       processor = new OptimizeImportsProcessor(processor);
     }
-    if (options.isRearrangeEntries()) {
+    if (options.isRearrangeCode()) {
       processor = new RearrangeCodeProcessor(processor);
     }
 
@@ -262,7 +211,7 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
                                      @NotNull ReformatFilesOptions selectedFlags)
   {
     boolean shouldOptimizeImports = selectedFlags.isOptimizeImports() && !DumbService.getInstance(project).isDumb();
-    boolean processOnlyChangedText = selectedFlags.isProcessOnlyChangedText();
+    boolean processOnlyChangedText = selectedFlags.getTextRangeType() == TextRangeType.VCS_CHANGED_TEXT;
 
     AbstractLayoutCodeProcessor processor;
     if (moduleContext != null)
@@ -277,7 +226,7 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
       processor = new OptimizeImportsProcessor(processor);
     }
 
-    if (selectedFlags.isRearrangeEntries()) {
+    if (selectedFlags.isRearrangeCode()) {
       processor = new RearrangeCodeProcessor(processor);
     }
 
@@ -329,23 +278,6 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
     }
   }
 
-  public static void updateShowDialogSetting(LayoutCodeDialog dialog, String title) {
-    if (dialog.isDoNotAskMe()) {
-      Notifications.Bus.notify(new Notification("Reformat Code", title,
-                                                "<html>You can re-enable the dialog on the <a href=''>IDE Settings -> Editor</a> pane</html>",
-                                                NotificationType.INFORMATION, new NotificationListener() {
-          @Override
-          public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent e) {
-            if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
-              final ShowSettingsUtil util = ShowSettingsUtil.getInstance();
-              IdeFrame ideFrame = WindowManagerEx.getInstanceEx().findFrameFor(null);
-              util.editConfigurable((JFrame)ideFrame, new EditorOptions());
-            }
-          }
-        }));
-    }
-  }
-
   public static PsiFile[] convertToPsiFiles(final VirtualFile[] files,Project project) {
     final PsiManager manager = PsiManager.getInstance(project);
     final ArrayList<PsiFile> result = new ArrayList<PsiFile>();
@@ -457,31 +389,6 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
     return dialog;
   }
 
-  @Nullable
-  private static LayoutCodeOptions getLayoutCodeOptions(@NotNull Project project,
-                                                 @Nullable PsiFile file,
-                                                 @Nullable PsiDirectory dir,
-                                                 boolean hasSelection) {
-    if (ApplicationManager.getApplication().isUnitTestMode()) {
-      return (LayoutCodeOptions)myTestOptions;
-    }
-    LayoutCodeDialog dialog = new LayoutCodeDialog(project, CodeInsightBundle.message("process.reformat.code"),
-                                                   file, dir, hasSelection ? Boolean.TRUE : Boolean.FALSE, HELP_ID);
-    if (!dialog.showAndGet()) {
-      return null;
-    }
-    EditorSettingsExternalizable.getInstance().getOptions().SHOW_REFORMAT_DIALOG = !dialog.isDoNotAskMe();
-    updateShowDialogSetting(dialog, "\"Reformat Code\" dialog disabled");
-    return dialog;
-  }
-
-  public static boolean getLastSavedRearrangeCbState(@NotNull Project project, @Nullable PsiFile file) {
-    if (file != null) {
-      return LayoutCodeSettingsStorage.getLastSavedRearrangeEntriesCbStateFor(project, file.getLanguage());
-    }
-    return LayoutCodeSettingsStorage.getLastSavedRearrangeEntriesCbStateFor(project);
-  }
-
   @TestOnly
   protected static void setTestOptions(ReformatFilesOptions options) {
     myTestOptions = options;
index 4ebe9274c1ca00202fce12e59f1ca2837e621b23..e9f80bd23fad04c71c52fbb85cf3dc55166fe747 100644 (file)
@@ -18,15 +18,14 @@ package com.intellij.codeInsight.actions;
 
 import com.intellij.codeInsight.CodeInsightBundle;
 import com.intellij.formatting.FormattingProgressTask;
-import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationType;
-import com.intellij.openapi.application.ApplicationBundle;
-import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.SelectionModel;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiDocumentManager;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.codeStyle.CodeStyleManager;
 import com.intellij.util.IncorrectOperationException;
@@ -46,13 +45,24 @@ public class ReformatCodeProcessor extends AbstractLayoutCodeProcessor {
   
   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.actions.ReformatCodeProcessor");
 
-  private final Collection<TextRange> myRanges = new ArrayList<TextRange>();
   private static final String PROGRESS_TEXT = CodeInsightBundle.message("reformat.progress.common.text");
+  private final Collection<TextRange> myRanges = new ArrayList<TextRange>();
+  private SelectionModel mySelectionModel;
 
   public ReformatCodeProcessor(Project project, boolean processChangedTextOnly) {
     super(project, COMMAND_NAME, PROGRESS_TEXT, processChangedTextOnly);
   }
 
+  public ReformatCodeProcessor(@NotNull PsiFile file, @NotNull SelectionModel selectionModel) {
+    super(file.getProject(), file, COMMAND_NAME, PROGRESS_TEXT, false);
+    mySelectionModel = selectionModel;
+  }
+
+  public ReformatCodeProcessor(AbstractLayoutCodeProcessor processor, @NotNull SelectionModel selectionModel) {
+    super(processor, COMMAND_NAME, PROGRESS_TEXT);
+    mySelectionModel = selectionModel;
+  }
+
   public ReformatCodeProcessor(AbstractLayoutCodeProcessor processor, boolean processChangedTextOnly) {
     super(processor, COMMAND_NAME, PROGRESS_TEXT);
     setProcessChangedTextOnly(processChangedTextOnly);
@@ -73,6 +83,10 @@ public class ReformatCodeProcessor extends AbstractLayoutCodeProcessor {
     }
   }
 
+  public ReformatCodeProcessor(@NotNull PsiFile file, boolean processChangedTextOnly) {
+    super(file.getProject(), file, PROGRESS_TEXT, COMMAND_NAME, processChangedTextOnly);
+  }
+
   public ReformatCodeProcessor(Project project, PsiFile[] files, @Nullable Runnable postRunnable, boolean processChangedTextOnly) {
     this(project, files, COMMAND_NAME, postRunnable, processChangedTextOnly);
   }
@@ -92,23 +106,31 @@ public class ReformatCodeProcessor extends AbstractLayoutCodeProcessor {
     throws IncorrectOperationException
   {
     return new FutureTask<Boolean>(new Callable<Boolean>() {
+      private Document myDocument;
+
       @Override
       public Boolean call() throws Exception {
         FormattingProgressTask.FORMATTING_CANCELLED_FLAG.set(false);
         try {
           Collection<TextRange> ranges = getRangesToFormat(processChangedTextOnly, file);
+
+          CharSequence before = null;
+          if (getInfoCollector() != null) {
+            myDocument = PsiDocumentManager.getInstance(myProject).getDocument(file);
+            LOG.assertTrue(myDocument != null);
+            before = myDocument.getImmutableCharSequence();
+          }
+
           CodeStyleManager.getInstance(myProject).reformatText(file, ranges);
+
+          if (before != null) {
+            prepareUserNotificationMessage(myDocument, before);
+          }
+
           return !FormattingProgressTask.FORMATTING_CANCELLED_FLAG.get();
         }
         catch (FilesTooBigForDiffException e) {
-          LOG.info("Error while calculating changed ranges for: " + file.getVirtualFile(), e);
-          if (!ApplicationManager.getApplication().isUnitTestMode()) {
-            Notification notification = new Notification(ApplicationBundle.message("reformat.changed.text.file.too.big.notification.groupId"),
-                                                         ApplicationBundle.message("reformat.changed.text.file.too.big.notification.title"),
-                                                         ApplicationBundle.message("reformat.changed.text.file.too.big.notification.text", file.getName()),
-                                                         NotificationType.INFORMATION);
-            notification.notify(file.getProject());
-          }
+          handleFileTooBigException(LOG, e, file);
           return false;
         } 
         catch (IncorrectOperationException e) {
@@ -122,8 +144,21 @@ public class ReformatCodeProcessor extends AbstractLayoutCodeProcessor {
     });
   }
 
+  private void prepareUserNotificationMessage(@NotNull Document document, @NotNull CharSequence before) {
+    LOG.assertTrue(getInfoCollector() != null);
+    int number = FormatChangedTextUtil.calculateChangedLinesNumber(document, before);
+    if (number > 0) {
+      String message = "formatted " + number + " line" + (number > 1 ? "s" : "");
+      getInfoCollector().setReformatCodeNotification(message);
+    }
+  }
+
   @NotNull
   private Collection<TextRange> getRangesToFormat(boolean processChangedTextOnly, PsiFile file) throws FilesTooBigForDiffException {
+    if (mySelectionModel != null) {
+      return getSelectedRanges(mySelectionModel);
+    }
+
     if (processChangedTextOnly) {
       return FormatChangedTextUtil.getChangedTextRanges(myProject, file);
     }
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeRunOptions.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeRunOptions.java
new file mode 100644 (file)
index 0000000..67d20e2
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.codeInsight.actions;
+
+public class ReformatCodeRunOptions implements LayoutCodeOptions {
+
+  private boolean myRearrangeCode;
+  private boolean myOptimizeImports;
+  private TextRangeType myProcessingScope;
+
+  public ReformatCodeRunOptions(TextRangeType processingScope) {
+    myProcessingScope = processingScope;
+  }
+
+  public void setProcessingScope(TextRangeType processingScope) {
+    myProcessingScope = processingScope;
+  }
+
+  @Override
+  public boolean isOptimizeImports() {
+    return myOptimizeImports;
+  }
+
+  @Override
+  public boolean isRearrangeCode() {
+    return myRearrangeCode;
+  }
+
+  public ReformatCodeRunOptions setRearrangeCode(boolean rearrangeCode) {
+    myRearrangeCode = rearrangeCode;
+    return this;
+  }
+
+  public ReformatCodeRunOptions setOptimizeImports(boolean optimizeImports) {
+    myOptimizeImports = optimizeImports;
+    return this;
+  }
+
+  @Override
+  public TextRangeType getTextRangeType() {
+    return myProcessingScope;
+  }
+
+}
+
index d364857352e6110b8ea67dcfdb0c7d7e89b9a2b9..a7651cd34cb595f62889d727d9baee9ef9fe6d45 100644 (file)
@@ -27,25 +27,27 @@ import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 
+import static com.intellij.codeInsight.actions.TextRangeType.*;
+
 public class ReformatFilesDialog extends DialogWrapper implements ReformatFilesOptions {
-  @NotNull private Project myProject;
   private JPanel myPanel;
   private JCheckBox myOptimizeImports;
   private JCheckBox myOnlyChangedText;
   private JCheckBox myRearrangeEntriesCb;
 
+  private final LastRunReformatCodeOptionsProvider myLastRunSettings;
+
   public ReformatFilesDialog(@NotNull Project project, @NotNull VirtualFile[] files) {
     super(project, true);
-    myProject = project;
-    setTitle(CodeInsightBundle.message("dialog.reformat.files.title"));
-    myOptimizeImports.setSelected(isOptmizeImportsOptionOn());
+    myLastRunSettings = new LastRunReformatCodeOptionsProvider(PropertiesComponent.getInstance());
+
     boolean canTargetVcsChanges = FormatChangedTextUtil.hasChanges(files, project);
     myOnlyChangedText.setEnabled(canTargetVcsChanges);
-    myOnlyChangedText.setSelected(
-      canTargetVcsChanges && PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, false)
-    ); 
-    myOptimizeImports.setSelected(isOptmizeImportsOptionOn());
-    myRearrangeEntriesCb.setSelected(LayoutCodeSettingsStorage.getLastSavedRearrangeEntriesCbStateFor(myProject));
+    myOnlyChangedText.setSelected(canTargetVcsChanges && myLastRunSettings.getLastTextRangeType() == VCS_CHANGED_TEXT);
+    myOptimizeImports.setSelected(myLastRunSettings.getLastOptimizeImports());
+    myRearrangeEntriesCb.setSelected(myLastRunSettings.getLastRearrangeCode());
+
+    setTitle(CodeInsightBundle.message("dialog.reformat.files.title"));
     init();
   }
 
@@ -60,25 +62,25 @@ public class ReformatFilesDialog extends DialogWrapper implements ReformatFilesO
   }
 
   @Override
-  public boolean isProcessOnlyChangedText() {
-    return myOnlyChangedText.isEnabled() && myOnlyChangedText.isSelected();
+  public TextRangeType getTextRangeType() {
+    return myOnlyChangedText.isEnabled() && myOnlyChangedText.isSelected()
+           ? VCS_CHANGED_TEXT
+           : WHOLE_FILE;
   }
 
   @Override
-  public boolean isRearrangeEntries() {
+  public boolean isRearrangeCode() {
     return myRearrangeEntriesCb.isSelected();
   }
 
   @Override
   protected void doOKAction() {
     super.doOKAction();
-    PropertiesComponent.getInstance().setValue(LayoutCodeConstants.OPTIMIZE_IMPORTS_KEY, Boolean.toString(myOptimizeImports.isSelected()));
-    PropertiesComponent.getInstance().setValue(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, Boolean.toString(myOnlyChangedText.isSelected()));
-    LayoutCodeSettingsStorage.saveRearrangeEntriesOptionFor(myProject, isRearrangeEntries());
-  }
-
-  static boolean isOptmizeImportsOptionOn() {
-    return PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.OPTIMIZE_IMPORTS_KEY, false);
+    myLastRunSettings.saveOptimizeImportsState(isOptimizeImports());
+    myLastRunSettings.saveRearrangeCodeState(isRearrangeCode());
+    if (myOnlyChangedText.isEnabled()) {
+      myLastRunSettings.saveProcessVcsChangedTextState(getTextRangeType() == VCS_CHANGED_TEXT);
+    }
   }
 
   @Nullable
index bb958e77b2fe729834b30395233ee718fbf3e388..f1075794032495cb8ad2e9c07458da1865a8ad2c 100644 (file)
@@ -18,13 +18,7 @@ package com.intellij.codeInsight.actions;
 import com.intellij.psi.search.SearchScope;
 import org.jetbrains.annotations.Nullable;
 
-public interface ReformatFilesOptions {
-
-  boolean isOptimizeImports();
-
-  boolean isProcessOnlyChangedText();
-
-  boolean isRearrangeEntries();
+public interface ReformatFilesOptions extends LayoutCodeOptions {
 
   @Nullable
   String getFileTypeMask();
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/ShowReformatFileDialog.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/ShowReformatFileDialog.java
new file mode 100644 (file)
index 0000000..b564b93
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.codeInsight.actions;
+
+import com.intellij.lang.LanguageFormatting;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NonNls;
+
+public class ShowReformatFileDialog extends AnAction implements DumbAware {
+  private static final @NonNls String HELP_ID = "editing.codeReformatting";
+
+  @Override
+  public void update(AnActionEvent event) {
+    Presentation presentation = event.getPresentation();
+    DataContext dataContext = event.getDataContext();
+    Project project = CommonDataKeys.PROJECT.getData(dataContext);
+    Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
+    if (project == null || editor == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
+    if (file == null || file.getVirtualFile() == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    if (LanguageFormatting.INSTANCE.forContext(file) != null) {
+      presentation.setEnabled(true);
+    }
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent event) {
+    Presentation presentation = event.getPresentation();
+    DataContext dataContext = event.getDataContext();
+    Project project = CommonDataKeys.PROJECT.getData(dataContext);
+    Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
+    if (project == null || editor == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
+    if (file == null || file.getVirtualFile() == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    boolean hasSelection = editor.getSelectionModel().hasSelection();
+    LayoutCodeDialog dialog = new LayoutCodeDialog(project, file, hasSelection, HELP_ID);
+    dialog.show();
+
+    if (dialog.isOK()) {
+      new FileInEditorProcessor(file, editor, dialog.getRunOptions()).processCode();
+    }
+  }
+}
index 21d21d91e48fc027e73e38b6868e94ebb5a55f6c..b3d39daede51baa0c0db17f52e76280ee8b28e1d 100644 (file)
@@ -57,6 +57,15 @@ import static com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens.Se
  * @since 7/20/12 1:56 PM
  */
 public class ArrangementEngine {
+  private boolean myCodeChanged;
+
+  @Nullable
+  public String getUserNotificationInfo() {
+    if (myCodeChanged) {
+      return "rearranged code";
+    }
+    return null;
+  }
 
   /**
    * Arranges given PSI root contents that belong to the given ranges.
@@ -87,8 +96,9 @@ public class ArrangementEngine {
    * @param file    target PSI root
    * @param ranges  target ranges to use within the given root
    */
-  @SuppressWarnings("MethodMayBeStatic")
   public void arrange(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges, @Nullable final ArrangementCallback callback) {
+    myCodeChanged = false;
+
     final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
     if (document == null) {
       return;
@@ -143,7 +153,7 @@ public class ArrangementEngine {
   }
 
   @SuppressWarnings("unchecked")
-  private static <E extends ArrangementEntry> void doArrange(Context<E> context) {
+  private <E extends ArrangementEntry> void doArrange(Context<E> context) {
     // The general idea is to process entries bottom-up where every processed group belongs to the same parent. We may not bother
     // with entries text ranges then. We use a list and a stack for achieving that than.
     //
@@ -363,8 +373,8 @@ public class ArrangementEngine {
   }
 
   @SuppressWarnings("unchecked")
-  private static <E extends ArrangementEntry> void doArrange(@NotNull List<ArrangementEntryWrapper<E>> wrappers,
-                                                             @NotNull Context<E> context) {
+  private <E extends ArrangementEntry> void doArrange(@NotNull List<ArrangementEntryWrapper<E>> wrappers,
+                                                      @NotNull Context<E> context) {
     if (wrappers.isEmpty()) {
       return;
     }
@@ -408,7 +418,9 @@ public class ArrangementEngine {
         if (previous != null && previous.equals(previousInitial) || previous == null && previousInitial == null) {
           final int beforeOffset = arrangedWrapper.getStartOffset();
           final int afterOffset = arrangedWrapper.getEndOffset();
-          context.changer.insertSection(context, arranged.get(i), newSectionsInfo, parentWrapper, beforeOffset, afterOffset);
+
+          boolean isInserted = context.changer.insertSection(context, arranged.get(i), newSectionsInfo, parentWrapper, beforeOffset, afterOffset);
+          myCodeChanged = isInserted || myCodeChanged;
           continue;
         }
       }
@@ -416,6 +428,7 @@ public class ArrangementEngine {
       ArrangementEntryWrapper<E> next = i < arranged.size() - 1 ? map.get(arranged.get(i + 1)) : null;
       context.changer.replace(arrangedWrapper, initialWrapper, previous, next, context);
       context.changer.insertSection(context, arranged.get(i), newSectionsInfo, arrangedWrapper, initialWrapper, parentWrapper);
+      myCodeChanged = true;
     }
   }
 
@@ -600,7 +613,7 @@ public class ArrangementEngine {
                                        @NotNull ArrangementEntryWrapper<E> initial,
                                        @Nullable ArrangementEntryWrapper<E> parent);
 
-    protected abstract void insertSection(@NotNull Context<E> context,
+    protected abstract boolean insertSection(@NotNull Context<E> context,
                                           @NotNull E entry,
                                           @NotNull NewSectionInfo<E> newSectionsInfo,
                                           @Nullable ArrangementEntryWrapper<E> parent, int beforeOffset, int afterOffset);
@@ -746,18 +759,22 @@ public class ArrangementEngine {
     }
 
     @Override
-    protected void insertSection(@NotNull Context<E> context,
+    protected boolean insertSection(@NotNull Context<E> context,
                                  @NotNull E entry,
                                  @NotNull NewSectionInfo<E> newSectionsInfo,
                                  ArrangementEntryWrapper<E> parent, int beforeOffset, int afterOffset) {
+      boolean isInserted = false;
       final String afterComment = newSectionsInfo.getEndComment(entry);
       if (afterComment != null) {
         insert(context, afterOffset, "\n" + afterComment);
+        isInserted = true;
       }
       final String beforeComment = newSectionsInfo.getStartComment(entry);
       if (beforeComment != null) {
         insert(context, beforeOffset, beforeComment + "\n");
+        isInserted = true;
       }
+      return isInserted;
     }
   }
 
@@ -886,24 +903,29 @@ public class ArrangementEngine {
     }
 
     @Override
-    protected void insertSection(@NotNull Context<E> context,
+    protected boolean insertSection(@NotNull Context<E> context,
                                  @NotNull E entry,
                                  @NotNull NewSectionInfo<E> newSectionsInfo,
                                  @Nullable ArrangementEntryWrapper<E> parent,
                                  int beforeOffset, int afterOffset) {
+      boolean isInserted = false;
       int diff = 0;
       final String afterComment = newSectionsInfo.getEndComment(entry);
       if (afterComment != null) {
         insert(context, afterOffset, "\n" + afterComment);
         diff += afterComment.length() + 1;
+        isInserted = true;
       }
       final String beforeComment = newSectionsInfo.getStartComment(entry);
       if (beforeComment != null) {
         insert(context, beforeOffset, beforeComment + "\n");
         diff += beforeComment.length() + 1;
+        isInserted = true;
       }
 
       updateAllWrapperRanges(parent, diff);
+
+      return isInserted;
     }
 
     /**
index df4aa168e8fa252e591e759445660643d5da3b91..727d96663ff5cb14a5a888348fb1ad0859c07df2 100644 (file)
@@ -548,6 +548,7 @@ action.OptimizeImports.text=Optimi_ze Imports...
 action.OptimizeImports.description=Remove unused imports and reorder/reorganize imports
 action.RearrangeCode.text=Rearrange Code
 action.RearrangeCode.description=Rearrange code
+action.ShowReformatFileDialog.text=Show Reformat File Dialog
 action.MoveStatementDown.text=Move Statement Do_wn
 action.MoveStatementDown.description=Move selected statements one line down
 action.MoveStatementUp.text=Move Statement _Up
index 4c4a14eb79b413aa20ecb0c91ee546a6d9e26e86..7cc0f894fe068c804804638a122628ede017139d 100644 (file)
@@ -7,6 +7,7 @@ reformat.directory.dialog.filters=Filters
 process.scope.directory=Directory ''{0}''
 process.scope.project=Project ''{0}''
 process.scope.module=Module ''{0}''
+process.scope.changed.files=only VCS changed files
 reformat.code.accept.button.text=Run
 process.scope.file=&File {0}
 reformat.option.selected.text=&Selected text
@@ -25,6 +26,7 @@ progress.reformat.and.optimize.common.command.text=Reformat and Optimize Imports
 progress.reformat.stage.wrapping.blocks=Preparing...
 progress.reformat.stage.processing.blocks=Calculating changes...
 progress.reformat.stage.applying.changes=Storing changes...
+process.rearrange.code=Rearranging code...
 process.reformat.code=Reformat Code
 process.reformat.code.before.commit=Reformat Code Before Commit
 process.rearrange.code.before.commit=Rearrange Code Before Commit
index 9f51364fa5a08551f0cd285af3d56d443d74e587..5ce4479f04915a974e26b892bc2a702df510e38a 100644 (file)
@@ -87,6 +87,9 @@
   <action id="ReformatCode">
     <keyboard-shortcut first-keystroke="control alt L"/>
   </action>
+  <action id="ShowReformatFileDialog">
+    <keyboard-shortcut first-keystroke="control shift alt L"/>
+  </action>
   <action id="Generate">
     <keyboard-shortcut first-keystroke="alt INSERT"/>
   </action>
index 9db13bc456b1d3219f266ab0347710f71168b964..cc37df8bfb689dc93d59419954e9364fa46fb717 100644 (file)
       <reference ref="Arrangement.Rule.Group.Condition.Move.Down"/>
     </group>
 
+    <action id="ShowReformatFileDialog" class="com.intellij.codeInsight.actions.ShowReformatFileDialog"/>
+
   </actions>
 </component>
diff --git a/platform/platform-tests/testData/codeStyle/formatter/addedLines.java b/platform/platform-tests/testData/codeStyle/formatter/addedLines.java
new file mode 100644 (file)
index 0000000..909aa31
--- /dev/null
@@ -0,0 +1,23 @@
+public class Add {
+
+  public void run() {
+    int a = 1;
+    a = 2;
+    a = 3;
+    a = 4;
+    a = 5;
+    a = 6;
+    a = 7;
+    a = 8;
+    a = 9;
+    a = 10;
+    a = 11;
+    a = 12;
+    a = 13;
+    a = 14;
+    a = 15;
+    a = 16;
+    a = 17;
+  }
+
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/addedLines_revision.java b/platform/platform-tests/testData/codeStyle/formatter/addedLines_revision.java
new file mode 100644 (file)
index 0000000..282dbcf
--- /dev/null
@@ -0,0 +1,7 @@
+public class Add {
+
+  public void run() {
+
+  }
+
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/changedAndDeleted.java b/platform/platform-tests/testData/codeStyle/formatter/changedAndDeleted.java
new file mode 100644 (file)
index 0000000..6c39c15
--- /dev/null
@@ -0,0 +1,9 @@
+public class ChangedAndDeleted {
+
+  public void run() {
+    String a = "12";
+    int f = 31;
+    int g = 33;
+    String b = "3";
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/changedAndDeleted_revision.java b/platform/platform-tests/testData/codeStyle/formatter/changedAndDeleted_revision.java
new file mode 100644 (file)
index 0000000..ea11b6b
--- /dev/null
@@ -0,0 +1,12 @@
+public class ChangedAndDeleted {
+
+  public void run() {
+    int a = 2;
+    int b = 3;
+    int c = 4;
+    int d = 12;
+    int e = 212;
+    int f = 31;
+    int g = 33;
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/changedSingleLine.java b/platform/platform-tests/testData/codeStyle/formatter/changedSingleLine.java
new file mode 100644 (file)
index 0000000..5433fda
--- /dev/null
@@ -0,0 +1,6 @@
+public class SingleLine {
+
+  public void run() {
+    String a = "1";
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/changedSingleLine_revision.java b/platform/platform-tests/testData/codeStyle/formatter/changedSingleLine_revision.java
new file mode 100644 (file)
index 0000000..3fab16a
--- /dev/null
@@ -0,0 +1,6 @@
+public class SingleLine {
+
+  public void run() {
+    int a = 2;
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/deletedLines.java b/platform/platform-tests/testData/codeStyle/formatter/deletedLines.java
new file mode 100644 (file)
index 0000000..4e04525
--- /dev/null
@@ -0,0 +1,7 @@
+public class DeletedLines {
+
+  public void run() {
+    int a = 1;
+    int e = 5;
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/deletedLines_revision.java b/platform/platform-tests/testData/codeStyle/formatter/deletedLines_revision.java
new file mode 100644 (file)
index 0000000..31f696d
--- /dev/null
@@ -0,0 +1,10 @@
+public class DeletedLines {
+
+  public void run() {
+    int a = 1;
+    int b = 2;
+    int c = 3;
+    int d = 4;
+    int e = 5;
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/insert.java b/platform/platform-tests/testData/codeStyle/formatter/insert.java
new file mode 100644 (file)
index 0000000..e34fdda
--- /dev/null
@@ -0,0 +1,10 @@
+public class Insert {
+
+  public void run() {
+    int a = 1;
+    int b = 3;
+    int c = 4;
+    String a = "aa";
+    String b = "bb";
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/insert_revision.java b/platform/platform-tests/testData/codeStyle/formatter/insert_revision.java
new file mode 100644 (file)
index 0000000..3495983
--- /dev/null
@@ -0,0 +1,8 @@
+public class Insert {
+
+  public void run() {
+    int a = 1;
+    int b = 3;
+    int c = 4;
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/lotsWhiteSpaces.java b/platform/platform-tests/testData/codeStyle/formatter/lotsWhiteSpaces.java
new file mode 100644 (file)
index 0000000..e085164
--- /dev/null
@@ -0,0 +1,6 @@
+public class WhiteSpaces {
+
+  public void run() {
+
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/lotsWhiteSpaces_revision.java b/platform/platform-tests/testData/codeStyle/formatter/lotsWhiteSpaces_revision.java
new file mode 100644 (file)
index 0000000..30257da
--- /dev/null
@@ -0,0 +1,25 @@
+public class WhiteSpaces {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  public void run() {
+
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/modification.java b/platform/platform-tests/testData/codeStyle/formatter/modification.java
new file mode 100644 (file)
index 0000000..f7ebb94
--- /dev/null
@@ -0,0 +1,8 @@
+public class Modify {
+
+  public void run() {
+    int a = 1;
+    String ad = "s";
+    int d = 6;
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/modification_revision.java b/platform/platform-tests/testData/codeStyle/formatter/modification_revision.java
new file mode 100644 (file)
index 0000000..647ce25
--- /dev/null
@@ -0,0 +1,10 @@
+public class Modify {
+
+  public void run() {
+    int a = 1;
+    int b = 3;
+    int c = 4;
+    int f = 5;
+    int d = 6;
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/modifiedLines.java b/platform/platform-tests/testData/codeStyle/formatter/modifiedLines.java
new file mode 100644 (file)
index 0000000..70fc21e
--- /dev/null
@@ -0,0 +1,11 @@
+public class Modified {
+
+  public void run() {
+    String a = "1";
+    String b = "2";
+    String c = "3";
+    String d = "4";
+    String e = "5";
+    String f = "6";
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testData/codeStyle/formatter/modifiedLines_revision.java b/platform/platform-tests/testData/codeStyle/formatter/modifiedLines_revision.java
new file mode 100644 (file)
index 0000000..a4b6033
--- /dev/null
@@ -0,0 +1,8 @@
+public class Modified {
+
+  public void run() {
+    int a = 1;
+    int b = 2;
+    int c = 3;
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/testSrc/com/intellij/codeInsight/actions/ChangedLinesCounterTest.java b/platform/platform-tests/testSrc/com/intellij/codeInsight/actions/ChangedLinesCounterTest.java
new file mode 100644 (file)
index 0000000..724e1e1
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.codeInsight.actions;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.psi.PsiFile;
+import com.intellij.testFramework.PlatformTestCase;
+import com.intellij.testFramework.PlatformTestUtil;
+import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
+import org.junit.Assert;
+
+import java.io.File;
+
+public class ChangedLinesCounterTest extends LightPlatformCodeInsightFixtureTestCase {
+
+  static {
+    PlatformTestCase.initPlatformLangPrefix();
+  }
+
+  @Override
+  protected String getTestDataPath() {
+    return PlatformTestUtil.getCommunityPath().replace(File.separatorChar, '/')
+           + "/platform/platform-tests/testData/"
+           + "codeStyle/formatter/";
+  }
+
+  @Override
+  public void tearDown() throws Exception {
+    myFixture.getFile().putUserData(FormatChangedTextUtil.TEST_REVISION_CONTENT, null);
+    super.tearDown();
+  }
+
+  public void doTest(int expectedLinesChanged) {
+    myFixture.configureByFile(getTestName(true) + "_revision.java");
+    PsiFile file = myFixture.getFile();
+    CharSequence revisionContent = myFixture.getDocument(file).getCharsSequence();
+
+    myFixture.configureByFile(getTestName(true) + ".java");
+    file = myFixture.getFile();
+    Document document = myFixture.getDocument(file);
+
+    int linesChanged = FormatChangedTextUtil.calculateChangedLinesNumber(document, revisionContent);
+    Assert.assertTrue(linesChanged > 0);
+    Assert.assertEquals(expectedLinesChanged, linesChanged);
+  }
+
+  public void testAddedLines() {
+    doTest(17);
+  }
+
+  public void testModifiedLines() {
+    doTest(6);
+  }
+
+  public void testDeletedLines() {
+    doTest(3);
+  }
+
+  public void testChangedSingleLine() {
+    doTest(1);
+  }
+
+  public void testChangedAndDeleted() {
+    doTest(6);
+  }
+
+  public void testModification() {
+    doTest(3);
+  }
+
+  public void testInsert() {
+    doTest(2);
+  }
+
+  public void testLotsWhiteSpaces() {
+    doTest(19);
+  }
+}