package krasa.formatter.plugin;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.impl.CheckUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.util.IncorrectOperationException;
import krasa.formatter.eclipse.EclipseCodeFormatterFacade;
import krasa.formatter.settings.Settings;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* Supported operations are handled by Eclipse formatter, other by IntelliJ formatter.
* <p/>
* TODO proper write action thread handling
*
* @author Vojtech Krasa
* @since 30.10.20011
*/
public class EclipseCodeStyleManager extends DelegatingCodeStyleManager {
public static final String GROUP_DISPLAY_ID = "Eclipse code formatter info";
public static final String GROUP_DISPLAY_ID_ERROR = "Eclipse code formatter error";
private static final Logger LOG = Logger.getInstance(EclipseCodeStyleManager.class.getName());
public static final String LINE_SEPARATOR = "\n";
@NotNull
private final Project project;
@NotNull
protected final EclipseCodeFormatterFacade codeFormatterFacade;
@NotNull
private Settings settings;
public EclipseCodeStyleManager(@NotNull CodeStyleManager original, @NotNull Settings settings, @NotNull Project project) {
super(original);
this.project = project;
this.settings = settings;
codeFormatterFacade = new EclipseCodeFormatterFacade(settings.getEclipsePrefs());
}
public void reformatText(@NotNull PsiFile psiFile, final int startOffset, final int endOffset) throws IncorrectOperationException {
boolean formattedByIntelliJ = false;
try {
ApplicationManager.getApplication().assertWriteAccessAllowed();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
CheckUtil.checkWritable(psiFile);
final VirtualFile file = psiFile.getVirtualFile();
Project project = psiFile.getProject();
final Editor editor = PsiUtilBase.findEditor(psiFile);
if (file != null && !canReformatWithEclipse(file, project)) {
formattedByIntelliJ = true;
super.reformatText(psiFile, startOffset, endOffset);
} else if (file != null) {
if (editor != null) {
// ApplicationManager.getApplication().runWriteAction(new Runnable() {
// @Override
// public void run() {
Document document = editor.getDocument();
String text = document.getText();
int fixedStartOffset = startOffset;
//if there is selected text
if (startOffset != 0) {
//start offset must be on the start of line
fixedStartOffset = text.substring(0, startOffset).lastIndexOf(LINE_SEPARATOR) + 1;
}
document.setText(codeFormatterFacade.format(text, fixedStartOffset, endOffset, LINE_SEPARATOR));
// }
// });
} else {
FileDocumentManager instance = FileDocumentManager.getInstance();
String iDocument = codeFormatterFacade.format(ioFile(file), LINE_SEPARATOR);
Document writeTo = instance.getDocument(file);
writeTo.setText(iDocument);
instance.saveDocument(writeTo);
}
} else {
notifyNothingWasFormatted();
return;
}
notifySuccessFormatting(psiFile, formattedByIntelliJ);
} catch (final Exception e) {
e.printStackTrace();
notifyFailedFormatting(psiFile, formattedByIntelliJ, e);
}
}
private void notifyNothingWasFormatted() {
Notification notification = new Notification(GROUP_DISPLAY_ID,
"",
"Nothing was not formatted",
NotificationType.WARNING);
showNotification(notification);
}
private void notifyFailedFormatting(PsiFile psiFile, boolean formattedByIntelliJ, Exception e) {
String error = e.getMessage() == null ? "" : e.getMessage();
if (!formattedByIntelliJ) {
Notification notification = new Notification(GROUP_DISPLAY_ID,
"",
psiFile.getName() + " failed to format with Eclipse code formatter. " + error,
NotificationType.ERROR);
showNotification(notification);
} else {
Notification notification = new Notification(GROUP_DISPLAY_ID,
"",
psiFile.getName() + " failed to format with IntelliJ code formatter. " + error,
NotificationType.ERROR);
showNotification(notification);
}
}
private void notifySuccessFormatting(PsiFile psiFile, boolean formattedByIntelliJ) {
if (formattedByIntelliJ) {
Notification notification = new Notification(GROUP_DISPLAY_ID,
"",
psiFile.getName() + " formatted sucessfully by IntelliJ code formatter",
NotificationType.WARNING);
showNotification(notification);
} else {
Notification notification = new Notification(GROUP_DISPLAY_ID,
"",
psiFile.getName() + " formatted sucessfully by Eclipse code formatter",
NotificationType.INFORMATION);
showNotification(notification);
}
}
private void showNotification(final Notification notification) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
Notifications.Bus.notify(notification, project);
}
});
}
private boolean canReformatWithEclipse(@NotNull VirtualFile file, @NotNull Project project) {
return file.isInLocalFileSystem()
&& isWritable(file, project)
&& fileTypeIsSupported(file);
}
private static boolean isWritable(@NotNull VirtualFile file, @NotNull Project project) {
return !ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(file).hasReadonlyFiles();
}
private boolean fileTypeIsSupported(@NotNull VirtualFile file) {
return ioFile(file).getPath().endsWith(".java");
}
@Deprecated //probably not needed anymore
private static boolean wholeFile(@NotNull PsiFile file, int startOffset, int endOffset) {
return startOffset == 0
&& endOffset == file.getTextRange().getEndOffset();
}
@NotNull
private static File ioFile(@NotNull VirtualFile file) {
return new File(file.getPath());
}
}