Merge branch 'no-reformat-dialog-on-reformat-code-action'

This commit is contained in:
Yaroslav Lepenkin
2015-02-20 17:15:21 +03:00
56 changed files with 1596 additions and 829 deletions
@@ -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;
}
};
}
@@ -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;
}
}
@@ -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;
}
}
@@ -0,0 +1,11 @@
public class Test {
int a = 2;
int b = 3;
public void run() {
int myFirst = 12;
}
}
@@ -0,0 +1,11 @@
public class Test {
<selection>int a = 2;
int b = 3;</selection>
public void run() {
int myFirst = 12;
}
}
@@ -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>();
}
}
@@ -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>();
}
}
@@ -0,0 +1,10 @@
public class Test {
public int a = 3;
public void test() {
int c = 3;
}
}
@@ -0,0 +1,10 @@
public class Test {
public int a = 3;
public void test() {
int c = 3;
}
}
@@ -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;
@@ -16,106 +16,90 @@
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);
String before = null;
if (options.isProcessOnlyChangedText()) {
myFixture.configureByFile(getTestName(true) + "_revision.java");
PsiFile file = myFixture.getFile();
Document document = myFixture.getDocument(file);
before = document.getText();
}
myFixture.configureByFile(getTestName(true) + "_before.java");
if (before != null) {
myFixture.getFile().putUserData(FormatChangedTextUtil.TEST_REVISION_CONTENT, before);
}
final String actionId = IdeActions.ACTION_EDITOR_REFORMAT;
AnAction action = ActionManager.getInstance().getAction(actionId);
AnActionEvent event = createEventFor(action, getProject(), myFixture.getEditor());
action.actionPerformed(event);
myFixture.checkResultByFile(getTestName(true) + "_after.java");
}
@Override
public void tearDown() throws Exception {
myFixture.getFile().putUserData(FormatChangedTextUtil.TEST_REVISION_CONTENT, null);
super.tearDown();
}
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 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);
revisionContent = document.getCharsSequence();
}
myFixture.configureByFile(getTestName(true) + "_before.java");
if (revisionContent != null) {
myFixture.getFile().putUserData(FormatChangedTextUtil.TEST_REVISION_CONTENT, revisionContent);
}
FileInEditorProcessor processor = new FileInEditorProcessor(myFixture.getFile(), myFixture.getEditor(), options);
processor.processCode();
myFixture.checkResultByFile(getTestName(true) + "_after.java");
}
protected void setOptions(ReformatFilesOptions options) {
ReformatCodeAction.setTestOptions(options);
public void testSelectionReformat() {
doTest(new ReformatCodeRunOptions(SELECTED_TEXT));
}
public void testWholeFileReformat() {
doTest(new ReformatCodeRunOptions(WHOLE_FILE));
}
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));
}
}
@@ -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));
}
}
}
@@ -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();
}
}
@@ -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();
}
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
return change != null;
}
final LineStatusTrackerManagerI manager = LineStatusTrackerManager.getInstance(project);
if (manager == null) {
return false;
}
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
@@ -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();
}
}
@@ -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>
@@ -16,279 +16,173 @@
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;
@NotNull private final PsiFile myFile;
private JRadioButton myRbFile;
private JRadioButton myRbSelectedText;
private JRadioButton myRbDirectory;
private JCheckBox myCbIncludeSubdirs;
private JCheckBox myCbOptimizeImports;
private JCheckBox myCbArrangeEntries;
private JCheckBox myCbOnlyVcsChangedRegions;
private JCheckBox myDoNotAskMeCheckBox;
private final boolean myTextSelected;
private final String myHelpId;
@Nullable private CommonCodeStyleSettings myCommonSettings;
private boolean myRearrangeAlwaysEnabled;
private final LastRunReformatCodeOptionsProvider myLastRunOptions;
private final boolean myOptimizeImportProcessorsForFileLanguageExists;
private final boolean myRearrangerProcessorsForFileLanguageExists;
private final boolean myFileHasChanges;
private JPanel myButtonsPanel;
private boolean myOptimizeImportsSelected;
private boolean myFormatOnlyVCSChangedRegionsSelected;
private boolean myDoNotShowDialogSelected;
private boolean myRearrangeEntriesSelected;
private JCheckBox myOptimizeImportsCb;
private JCheckBox myRearrangeCodeCb;
private JRadioButton myOnlyVCSChangedTextRb;
private JRadioButton mySelectedTextRadioButton;
private JRadioButton myWholeFileRadioButton;
public LayoutCodeDialog(@NotNull Project project,
@NotNull String title,
@Nullable PsiFile file,
@Nullable PsiDirectory directory,
Boolean isTextSelected,
private JPanel myActionsPanel;
private JPanel myScopePanel;
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;
myTextSelected = textSelected;
myHelpId = helpId;
myOptimizeImportProcessorsForFileLanguageExists = myFile != null && !LanguageImportStatements.INSTANCE.forFile(myFile).isEmpty();
myRearrangerProcessorsForFileLanguageExists = myFile != null && Rearranger.EXTENSION.forLanguage(myFile.getLanguage()) != null;
myFileHasChanges = myFile != null && FormatChangedTextUtil.hasChanges(myFile);
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();
myRbFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
saveEnabledCbsSelectedState();
setUpCbsStateForFileFormatting();
}
});
myRbDirectory.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
saveEnabledCbsSelectedState();
setUpCbsStatesForDirectoryFormatting();
}
});
myRbSelectedText.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
saveEnabledCbsSelectedState();
setUpCbsStatesForSelectedTextFormatting();
}
});
setUpActions();
setUpTextRangeMode();
}
private void setUpInitialSelection() {
if (myTextSelected == Boolean.TRUE) {
myRbSelectedText.setSelected(true);
setUpCbsStatesForSelectedTextFormatting();
private void setUpTextRangeMode() {
mySelectedTextRadioButton.setEnabled(myTextSelected);
if (!myTextSelected) {
mySelectedTextRadioButton.setToolTipText("No text selected in editor");
}
final boolean fileHasChanges = FormatChangedTextUtil.hasChanges(myFile);
if (myFile.getVirtualFile() instanceof LightVirtualFile) {
myOnlyVCSChangedTextRb.setVisible(false);
}
else {
if (myFile != null) {
myRbFile.setSelected(true);
setUpCbsStateForFileFormatting();
myOnlyVCSChangedTextRb.setEnabled(fileHasChanges);
if (!fileHasChanges) {
String hint = getChangesNotAvailableHint();
if (hint != null) myOnlyVCSChangedTextRb.setToolTipText(hint);
}
}
myWholeFileRadioButton.setEnabled(true);
if (myTextSelected) {
mySelectedTextRadioButton.setSelected(true);
}
else {
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 setUpActions() {
boolean canOptimizeImports = !LanguageImportStatements.INSTANCE.forFile(myFile).isEmpty();
myOptimizeImportsCb.setVisible(canOptimizeImports);
if (canOptimizeImports) {
myOptimizeImportsCb.setSelected(myLastRunOptions.getLastOptimizeImports());
}
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();
boolean canRearrangeCode = Rearranger.EXTENSION.forLanguage(myFile.getLanguage()) != null;
myRearrangeCodeCb.setVisible(canRearrangeCode);
if (canRearrangeCode) {
myRearrangeCodeCb.setSelected(myLastRunOptions.isRearrangeCode(myFile.getLanguage()));
}
}
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);
@Nullable
private String getChangesNotAvailableHint() {
if (!VcsUtil.isFileUnderVcs(myProject, VcsUtil.getFilePath(myFile.getVirtualFile()))) {
return "File not under VCS root";
}
else if (!FormatChangedTextUtil.hasChanges(myFile)) {
return "File was not changed since last revision";
}
return null;
}
private void setUpCbsStatesForDirectoryFormatting() {
myCbOptimizeImports.setEnabled(true);
myCbOptimizeImports.setSelected(myOptimizeImportsSelected);
private void saveCurrentConfiguration() {
if (myOptimizeImportsCb.isEnabled()) {
myLastRunOptions.saveOptimizeImportsState(myRunOptions.isOptimizeImports());
}
if (myRearrangeCodeCb.isEnabled()) {
myLastRunOptions.saveRearrangeState(myFile.getLanguage(), myRunOptions.isRearrangeCode());
}
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);
if (!mySelectedTextRadioButton.isSelected()) {
myLastRunOptions.saveProcessVcsChangedTextState(myOnlyVCSChangedTextRb.isSelected());
}
}
private void setUpCbsStatesForSelectedTextFormatting() {
myCbOptimizeImports.setEnabled(false);
myCbOptimizeImports.setSelected(false);
@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;
}
myCbArrangeEntries.setEnabled(true);
myCbArrangeEntries.setSelected(myRearrangeEntriesSelected);
@Override
public boolean isRearrangeCode() {
return myRearrangeCodeCb.isEnabled() && myRearrangeCodeCb.isSelected();
}
myCbOnlyVcsChangedRegions.setEnabled(false);
myCbOnlyVcsChangedRegions.setSelected(false);
myDoNotAskMeCheckBox.setEnabled(true);
myDoNotAskMeCheckBox.setSelected(myDoNotShowDialogSelected);
myCbIncludeSubdirs.setEnabled(false);
@Override
public boolean isOptimizeImports() {
return myOptimizeImportsCb.isEnabled() && myOptimizeImportsCb.isSelected();
}
};
}
@Nullable
@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);
}
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);
}
}
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);
}
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);
}
myCbOnlyVcsChangedRegions = new JCheckBox(CodeInsightBundle.message("reformat.option.vcs.changed.region"));
gbConstraints.gridy++;
panel.add(myCbOnlyVcsChangedRegions, gbConstraints);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(myRbFile);
buttonGroup.add(myRbSelectedText);
buttonGroup.add(myRbDirectory);
myRbFile.setEnabled(myFile != null);
myRbSelectedText.setEnabled(myTextSelected == Boolean.TRUE);
return panel;
}
@Override
protected JComponent createSouthPanel() {
JComponent southPanel = super.createSouthPanel();
myDoNotAskMeCheckBox = new JCheckBox(CommonBundle.message("dialog.options.do.not.show"));
return DialogWrapper.addDoNotShowCheckBox(southPanel, myDoNotAskMeCheckBox);
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);
}
}
@@ -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;
}
}
@@ -15,10 +15,14 @@
*/
package com.intellij.codeInsight.actions;
public interface LayoutCodeOptions extends DirectoryFormattingOptions {
public interface LayoutCodeOptions extends OptionalReformatActions {
boolean isProcessWholeFile();
boolean isProcessDirectory();
TextRangeType getTextRangeType();
}
enum TextRangeType {
VCS_CHANGED_TEXT,
SELECTED_TEXT,
WHOLE_FILE
}
@@ -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();
}
}
@@ -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;
}
}
@@ -17,11 +17,11 @@
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 final String myText;
private static class OptimizeImportsDialog extends DialogWrapper {
private final boolean myContextHasChanges;
OptimizeOnModuleDialog(Project project, String text) {
private final String myText;
private JCheckBox myOnlyVcsCheckBox;
private final LastRunReformatCodeOptionsProvider myLastRunOptions;
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;
}
}
}
@@ -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;
}
}
}
@@ -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.
@@ -15,18 +15,10 @@
*/
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() {
}
}
@@ -15,8 +15,10 @@
*/
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);
try {
Collection<TextRange> ranges = getRangesToFormat(file, processChangedTextOnly);
Document document = PsiDocumentManager.getInstance(myProject).getDocument(file);
RearrangeCommand rearranger = new RearrangeCommand(myProject, file, COMMAND_NAME, ranges);
if (rearranger.couldRearrange()) {
rearranger.run();
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());
}
}
@@ -16,32 +16,22 @@
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();
LastRunReformatCodeOptionsProvider provider = new LastRunReformatCodeOptionsProvider(PropertiesComponent.getInstance());
ReformatCodeRunOptions currentRunOptions = provider.getLastRunOptions(file);
if (selectedFlags.isProcessDirectory()) {
assert dir != null : "File = " + file + ", Element = " + CommonDataKeys.PSI_ELEMENT.getData(dataContext);
reformatDirectory(project, dir, selectedFlags);
return;
TextRangeType processingScope = currentRunOptions.getTextRangeType();
if (hasSelection) {
processingScope = TextRangeType.SELECTED_TEXT;
}
else if (processingScope == TextRangeType.VCS_CHANGED_TEXT) {
if (isChangeNotTrackedForFile(project, file)) {
processingScope = TextRangeType.WHOLE_FILE;
}
}
if (!showDialog && processChangedTextOnly && isChangeNotTrackedForFile(project, file)) {
processChangedTextOnly = false;
}
final TextRange range;
final boolean processSelectedText = !processWholeFile && hasSelection;
if (processSelectedText) {
range = TextRange.create(editor.getSelectionModel().getSelectionStart(), editor.getSelectionModel().getSelectionEnd());
}
else{
range = null;
}
AbstractLayoutCodeProcessor processor;
if (optimizeImports && range == null) {
processor = new OptimizeImportsProcessor(project, file);
processor = new ReformatCodeProcessor(processor, processChangedTextOnly);
}
else {
processor = new ReformatCodeProcessor(project, file, range, !processSelectedText && processChangedTextOnly);
processingScope = TextRangeType.WHOLE_FILE;
}
if (rearrangeEntries) {
if (processSelectedText && editor != null) {
processor = new RearrangeCodeProcessor(processor, editor.getSelectionModel());
}
else {
processor = new RearrangeCodeProcessor(processor);
}
}
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;
@@ -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);
}
@@ -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;
}
}
@@ -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
@@ -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();
@@ -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();
}
}
}
@@ -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;
}
/**
@@ -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
@@ -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
@@ -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>
@@ -938,5 +938,7 @@
<reference ref="Arrangement.Rule.Group.Condition.Move.Down"/>
</group>
<action id="ShowReformatFileDialog" class="com.intellij.codeInsight.actions.ShowReformatFileDialog"/>
</actions>
</component>
@@ -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;
}
}
@@ -0,0 +1,7 @@
public class Add {
public void run() {
}
}
@@ -0,0 +1,9 @@
public class ChangedAndDeleted {
public void run() {
String a = "12";
int f = 31;
int g = 33;
String b = "3";
}
}
@@ -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;
}
}
@@ -0,0 +1,6 @@
public class SingleLine {
public void run() {
String a = "1";
}
}
@@ -0,0 +1,6 @@
public class SingleLine {
public void run() {
int a = 2;
}
}
@@ -0,0 +1,7 @@
public class DeletedLines {
public void run() {
int a = 1;
int e = 5;
}
}
@@ -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;
}
}
@@ -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";
}
}
@@ -0,0 +1,8 @@
public class Insert {
public void run() {
int a = 1;
int b = 3;
int c = 4;
}
}
@@ -0,0 +1,6 @@
public class WhiteSpaces {
public void run() {
}
}
@@ -0,0 +1,25 @@
public class WhiteSpaces {
public void run() {
}
}
@@ -0,0 +1,8 @@
public class Modify {
public void run() {
int a = 1;
String ad = "s";
int d = 6;
}
}
@@ -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;
}
}
@@ -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";
}
}
@@ -0,0 +1,8 @@
public class Modified {
public void run() {
int a = 1;
int b = 2;
int c = 3;
}
}
@@ -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);
}
}