diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/ChangeFileEncodingAction.java b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/ChangeFileEncodingAction.java index 3a5bc48fce85..271625054c58 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/ChangeFileEncodingAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/ChangeFileEncodingAction.java @@ -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 diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/ChooseFileEncodingAction.java b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/ChooseFileEncodingAction.java index cca16ea1a1d9..6cccaa5d6982 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/ChooseFileEncodingAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/ChooseFileEncodingAction.java @@ -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 myText = new AtomicReference<>(); + AtomicReference 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); } }; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingUtil.java b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingUtil.java index 192f71cf10f5..3b58d3cb5e9c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingUtil.java @@ -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 chosen = LoadTextUtil.chooseMostlyHarmlessCharset(virtualFile.getCharset(), charset, textToSave); + Pair 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;