calculate warnings icons in background to fix IDEA-171254 IDEA freezes when I click on encoding panel for big file

This commit is contained in:
Alexey Kudravtsev
2017-04-18 14:51:08 +03:00
parent 341b2116cf
commit b047a2f353
3 changed files with 44 additions and 17 deletions
@@ -121,16 +121,8 @@ public class ChangeFileEncodingAction extends AnAction implements DumbAware {
@NotNull
@Override
protected DefaultActionGroup createPopupActionGroup(JComponent button) {
return createCharsetsActionGroup(clearItemText, null, charset -> {
assert myFile == null || myFile.isDirectory() || text != null : charset;
EncodingUtil.Magic8 safeToReload = myFile == null || myFile.isDirectory() ? EncodingUtil.Magic8.ABSOLUTELY : EncodingUtil.isSafeToReloadIn(myFile, text, bytes, charset);
boolean enabled = safeToReload != EncodingUtil.Magic8.NO_WAY;
if (!enabled) {
EncodingUtil.Magic8 safeToConvert = myFile.isDirectory() ? EncodingUtil.Magic8.ABSOLUTELY : EncodingUtil.isSafeToConvertTo(myFile, text, bytes, charset);
enabled = safeToConvert != EncodingUtil.Magic8.NO_WAY;
}
return enabled ? "Change encoding to '"+charset.displayName()+"'" : null;
}); // no 'clear'
return createCharsetsActionGroup(clearItemText, null, charset -> "Change encoding to '" + charset.displayName() + "'");
// no 'clear'
}
@Override
@@ -27,14 +27,20 @@ import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.IconDeferrer;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.ui.EmptyIcon;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
@@ -42,6 +48,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
public abstract class ChooseFileEncodingAction extends ComboBoxAction {
private final VirtualFile myVirtualFile;
@@ -68,7 +75,34 @@ public abstract class ChooseFileEncodingAction extends ComboBoxAction {
public void update(AnActionEvent e) {
super.update(e);
String description = charsetFilter.fun(charset);
e.getPresentation().setIcon(description == null ? AllIcons.General.Warning : null);
AtomicReference<CharSequence> myText = new AtomicReference<>();
AtomicReference<byte[]> myBytes = new AtomicReference<>();
Icon defer = virtualFile == null || virtualFile.isDirectory() ? null : IconDeferrer.getInstance().defer(null, Pair.create(virtualFile, charset), pair -> {
VirtualFile myFile = pair.getFirst();
Charset charset = pair.getSecond();
CharSequence text = myText.get();
if (text == null) {
myText.set(text = LoadTextUtil.loadText(myFile));
}
byte[] bytes = myBytes.get();
if (bytes == null) {
try {
myBytes.set(bytes = myFile.contentsToByteArray());
}
catch (IOException io) {
bytes = ArrayUtil.EMPTY_BYTE_ARRAY;
}
}
EncodingUtil.Magic8 safeToReload = EncodingUtil.isSafeToReloadIn(myFile, text, bytes, charset);
EncodingUtil.Magic8 safeToConvert = EncodingUtil.Magic8.ABSOLUTELY;
if (safeToReload != EncodingUtil.Magic8.ABSOLUTELY) {
safeToConvert = EncodingUtil.isSafeToConvertTo(myFile, text, bytes, charset);
}
return safeToReload == EncodingUtil.Magic8.ABSOLUTELY || safeToConvert == EncodingUtil.Magic8.ABSOLUTELY ? null :
safeToReload == EncodingUtil.Magic8.WELL_IF_YOU_INSIST || safeToConvert == EncodingUtil.Magic8.WELL_IF_YOU_INSIST ?
AllIcons.General.Warning : AllIcons.General.Error;
});
e.getPresentation().setIcon(defer);
e.getPresentation().setDescription(description);
}
};
@@ -33,6 +33,7 @@ import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.openapi.vfs.*;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.util.ArrayUtil;
@@ -62,7 +63,7 @@ public class EncodingUtil {
// returns NO_WAY if the new encoding is incompatible (bytes on disk will differ)
// returns WELL_IF_YOU_INSIST if the bytes on disk remain the same but the text will change
@NotNull
static Magic8 isSafeToReloadIn(@NotNull VirtualFile virtualFile, @NotNull String text, @NotNull byte[] bytes, @NotNull Charset charset) {
static Magic8 isSafeToReloadIn(@NotNull VirtualFile virtualFile, @NotNull CharSequence text, @NotNull byte[] bytes, @NotNull Charset charset) {
// file has BOM but the charset hasn't
byte[] bom = virtualFile.getBOM();
if (bom != null && !CharsetToolkit.canHaveBom(charset, bom)) return Magic8.NO_WAY;
@@ -92,22 +93,22 @@ public class EncodingUtil {
bytesToSave = ArrayUtil.mergeArrays(bom, bytesToSave); // for 2-byte encodings String.getBytes(Charset) adds BOM automatically
}
return !Arrays.equals(bytesToSave, bytes) ? Magic8.NO_WAY : loaded.equals(text) ? Magic8.ABSOLUTELY : Magic8.WELL_IF_YOU_INSIST;
return !Arrays.equals(bytesToSave, bytes) ? Magic8.NO_WAY : StringUtil.equals(loaded, text) ? Magic8.ABSOLUTELY : Magic8.WELL_IF_YOU_INSIST;
}
@NotNull
static Magic8 isSafeToConvertTo(@NotNull VirtualFile virtualFile, @NotNull String text, @NotNull byte[] bytesOnDisk, @NotNull Charset charset) {
static Magic8 isSafeToConvertTo(@NotNull VirtualFile virtualFile, @NotNull CharSequence text, @NotNull byte[] bytesOnDisk, @NotNull Charset charset) {
try {
String lineSeparator = FileDocumentManager.getInstance().getLineSeparator(virtualFile, null);
String textToSave = lineSeparator.equals("\n") ? text : StringUtil.convertLineSeparators(text, lineSeparator);
CharSequence textToSave = lineSeparator.equals("\n") ? text : StringUtilRt.convertLineSeparators(text, lineSeparator);
Pair<Charset, byte[]> chosen = LoadTextUtil.chooseMostlyHarmlessCharset(virtualFile.getCharset(), charset, textToSave);
Pair<Charset, byte[]> chosen = LoadTextUtil.chooseMostlyHarmlessCharset(virtualFile.getCharset(), charset, textToSave.toString());
byte[] saved = chosen.second;
CharSequence textLoadedBack = LoadTextUtil.getTextByBinaryPresentation(saved, charset);
return !text.equals(textLoadedBack.toString()) ? Magic8.NO_WAY : Arrays.equals(saved, bytesOnDisk) ? Magic8.ABSOLUTELY : Magic8.WELL_IF_YOU_INSIST;
return !StringUtil.equals(text, textLoadedBack) ? Magic8.NO_WAY : Arrays.equals(saved, bytesOnDisk) ? Magic8.ABSOLUTELY : Magic8.WELL_IF_YOU_INSIST;
}
catch (UnsupportedOperationException e) { // unsupported encoding
return Magic8.NO_WAY;