encoding fixes: IDEA-99729, IDEA-99926, IDEA-99809, IDEA-99945

This commit is contained in:
Alexey Kudravtsev
2013-02-05 10:30:16 +04:00
parent ab2a47cc4d
commit b95f94162b
12 changed files with 204 additions and 143 deletions

View File

@@ -52,7 +52,9 @@ public class LossyEncodingTest extends LightDaemonAnalyzerTestCase {
public void testText() throws Exception {
doTest("Text.txt");
EncodingManager.getInstance().setEncoding(myVFile, Charset.forName("US-ASCII"));
Charset ascii = CharsetToolkit.forName("US-ASCII");
EncodingManager.getInstance().setEncoding(myVFile, ascii);
assertEquals(ascii, myVFile.getCharset());
int start = myEditor.getCaretModel().getOffset();
type((char)0x445);
type((char)0x438);

View File

@@ -22,11 +22,14 @@
*/
package com.intellij.codeInspection;
import com.intellij.ide.DataManager;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.properties.charset.Native2AsciiCharset;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
@@ -38,7 +41,6 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.encoding.ChooseFileEncodingAction;
import com.intellij.openapi.vfs.encoding.ReloadFileInOtherEncodingAction;
import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.openapi.wm.impl.status.EncodingActionsPair;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.util.ArrayUtil;
@@ -48,6 +50,7 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
@@ -215,10 +218,22 @@ public class LossyEncodingInspection extends LocalInspectionTool {
VirtualFile virtualFile = psiFile.getVirtualFile();
Editor editor = PsiUtilBase.findEditor(psiFile);
DataContext dataContext =
EncodingActionsPair.createDataContext(editor, editor == null ? null : editor.getComponent(), virtualFile, project);
DataContext dataContext = createDataContext(editor, editor == null ? null : editor.getComponent(), virtualFile, project);
ReloadFileInOtherEncodingAction reloadAction = new ReloadFileInOtherEncodingAction();
reloadAction.actionPerformed(new AnActionEvent(null, dataContext, "", reloadAction.getTemplatePresentation(), ActionManager.getInstance(), 0));
}
@NotNull
public static DataContext createDataContext(Editor editor, Component component, VirtualFile selectedFile, Project project) {
DataContext parent = DataManager.getInstance().getDataContext(component);
return SimpleDataContext.getSimpleContext(PlatformDataKeys.VIRTUAL_FILE.getName(), selectedFile,
SimpleDataContext.getSimpleContext(PlatformDataKeys.PROJECT.getName(), project,
SimpleDataContext.getSimpleContext(
PlatformDataKeys.CONTEXT_COMPONENT.getName(),
editor == null ? null : editor.getComponent(),
parent)));
}
}
}

View File

@@ -28,6 +28,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileFilter;
import com.intellij.util.Function;
import com.intellij.util.ui.tree.AbstractFileTreeTable;
import org.jetbrains.annotations.NotNull;
@@ -94,7 +95,12 @@ class EncodingFileTreeTable extends AbstractFileTreeTable<Charset> {
@NotNull
@Override
protected DefaultActionGroup createPopupActionGroup(JComponent button) {
return createGroup("<Clear>", "Encoding ''{1}''", null, null);
return createGroup("<Clear>", null, new Function<Charset, String>() {
@Override
public String fun(Charset charset) {
return "Choose encoding '"+charset+"'";
}
});
}
@Override

View File

@@ -32,6 +32,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.table.JBTable;
import com.intellij.util.Function;
import com.intellij.util.PlatformUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
@@ -109,7 +110,12 @@ public class FileEncodingConfigurable implements SearchableConfigurable, Optiona
@NotNull
@Override
protected DefaultActionGroup createPopupActionGroup(JComponent button) {
return createGroup("<System Default>", "Choose encoding ''{1}''", selected.get(), null);
return createGroup("<System Default>", selected.get(), new Function<Charset, String>() {
@Override
public String fun(Charset charset) {
return "Choose encoding '"+charset+"'";
}
});
}
};
parentPanel.removeAll();
@@ -161,7 +167,10 @@ public class FileEncodingConfigurable implements SearchableConfigurable, Optiona
@Override
public void apply() throws ConfigurationException {
Charset projectCharset = mySelectedProjectCharset.get();
Map<VirtualFile,Charset> result = myTreeView.getValues();
result.put(null, projectCharset);
EncodingProjectManager encodingManager = EncodingProjectManager.getInstance(myProject);
encodingManager.setMapping(result);
encodingManager.setDefaultCharsetForPropertiesFiles(null, mySelectedCharsetForPropertiesFiles.get());
@@ -170,8 +179,6 @@ public class FileEncodingConfigurable implements SearchableConfigurable, Optiona
Charset ideCharset = mySelectedIdeCharset.get();
EncodingManager.getInstance().setDefaultCharsetName(ideCharset == null ? "" : ideCharset.name());
Charset projectCharset = mySelectedProjectCharset.get();
EncodingProjectManager.getInstance(myProject).setEncoding(null, projectCharset);
}
@Override

View File

@@ -23,7 +23,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.charset.Charset;
import java.text.MessageFormat;
/**
* @author cdr
@@ -32,8 +31,8 @@ abstract class ChangeFileEncodingTo extends AnAction implements DumbAware {
private final VirtualFile myFile;
private final Charset myCharset;
ChangeFileEncodingTo(@Nullable VirtualFile file, @NotNull Charset charset, @NotNull String pattern) {
super(charset.displayName(), MessageFormat.format(pattern, file == null ? null : file.getName(), charset.displayName()), null);
ChangeFileEncodingTo(@Nullable VirtualFile file, @NotNull Charset charset) {
super(charset.displayName());
myFile = file;
myCharset = charset;
}

View File

@@ -33,10 +33,10 @@ import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -81,14 +81,17 @@ public abstract class ChooseFileEncodingAction extends ComboBoxAction {
private void fillCharsetActions(@NotNull DefaultActionGroup group,
@Nullable VirtualFile virtualFile,
@NotNull List<Charset> charsets,
@Nullable final Condition<Charset> charsetFilter,
@NotNull String pattern) {
@NotNull final Function<Charset, String> charsetFilter) {
for (final Charset slave : charsets) {
ChangeFileEncodingTo action = new ChangeFileEncodingTo(virtualFile, slave, pattern) {
ChangeFileEncodingTo action = new ChangeFileEncodingTo(virtualFile, slave) {
{
if (charsetFilter != null && !charsetFilter.value(slave)) {
String description = charsetFilter.fun(slave);
if (description == null) {
getTemplatePresentation().setIcon(AllIcons.General.Warning);
}
else {
getTemplatePresentation().setDescription(description);
}
}
@Override
@@ -170,9 +173,8 @@ public abstract class ChooseFileEncodingAction extends ComboBoxAction {
@NotNull
public DefaultActionGroup createGroup(@Nullable("null means do not show 'clear' text") String clearItemText,
@NotNull String pattern,
Charset alreadySelected,
@Nullable Condition<Charset> charsetFilter) {
@NotNull Function<Charset, String> charsetFilter) {
DefaultActionGroup group = new DefaultActionGroup();
List<Charset> favorites = new ArrayList<Charset>(EncodingManager.getInstance().getFavorites());
Collections.sort(favorites);
@@ -184,14 +186,14 @@ public abstract class ChooseFileEncodingAction extends ComboBoxAction {
group.add(new ClearThisFileEncodingAction(myVirtualFile, clearItemText));
}
if (favorites.isEmpty() && clearItemText == null) {
fillCharsetActions(group, myVirtualFile, Arrays.asList(CharsetToolkit.getAvailableCharsets()), charsetFilter, pattern);
fillCharsetActions(group, myVirtualFile, Arrays.asList(CharsetToolkit.getAvailableCharsets()), charsetFilter);
}
else {
fillCharsetActions(group, myVirtualFile, favorites, charsetFilter, pattern);
fillCharsetActions(group, myVirtualFile, favorites, charsetFilter);
DefaultActionGroup more = new DefaultActionGroup("more", true);
group.add(more);
fillCharsetActions(more, myVirtualFile, Arrays.asList(CharsetToolkit.getAvailableCharsets()), charsetFilter, pattern);
fillCharsetActions(more, myVirtualFile, Arrays.asList(CharsetToolkit.getAvailableCharsets()), charsetFilter);
}
return group;
}

View File

@@ -45,6 +45,7 @@ import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.UIUtil;
@@ -215,26 +216,21 @@ public class EncodingProjectManagerImpl extends EncodingProjectManager {
myMapping.put(virtualFileOrDir, charset);
}
myModificationCount++;
reloadDir(virtualFileOrDir, charset);
if (virtualFileOrDir != null && !virtualFileOrDir.isDirectory()) {
virtualFileOrDir.setCharset(null);
}
reloadAllFilesUnder(virtualFileOrDir);
}
private static void setAndSaveOrReload(@NotNull VirtualFile virtualFileOrDir, Charset charset) {
virtualFileOrDir.setCharset(charset);
saveOrReload(virtualFileOrDir);
private static void clearAndReload(@NotNull VirtualFile virtualFileOrDir) {
virtualFileOrDir.setCharset(null);
reload(virtualFileOrDir);
}
private static void saveOrReload(@NotNull VirtualFile virtualFile) {
private static void reload(@NotNull VirtualFile virtualFile) {
FileDocumentManager documentManager = FileDocumentManager.getInstance();
Document document = documentManager.getDocument(virtualFile);
if (documentManager.isFileModified(virtualFile)) {
if (document != null) {
documentManager.saveDocument(document);
}
}
else {
((VirtualFileListener)documentManager)
.contentsChanged(new VirtualFileEvent(null, virtualFile, virtualFile.getName(), virtualFile.getParent()));
}
((VirtualFileListener)documentManager)
.contentsChanged(new VirtualFileEvent(null, virtualFile, virtualFile.getName(), virtualFile.getParent()));
}
@Override
@@ -263,6 +259,7 @@ public class EncodingProjectManagerImpl extends EncodingProjectManager {
@Override
public void setMapping(@NotNull final Map<VirtualFile, Charset> result) {
ApplicationManager.getApplication().assertIsDispatchThread();
FileDocumentManager.getInstance().saveAllDocuments(); // consider all files as unmodified
final Map<VirtualFile, Charset> map = new HashMap<VirtualFile, Charset>(result.size());
ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
for (Map.Entry<VirtualFile, Charset> entry : result.entrySet()) {
@@ -284,16 +281,17 @@ public class EncodingProjectManagerImpl extends EncodingProjectManager {
myMapping.clear();
myMapping.putAll(map);
final Processor<VirtualFile> reloadProcessor = createChangeCharsetProcessor();
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
Set<VirtualFile> changed = new HashSet<VirtualFile>(map.keySet());
changed.addAll(oldMapping.keySet());
for (VirtualFile changedFile : changed) {
Charset newCharset = map.get(changedFile);
final Charset newCharset = map.get(changedFile);
Charset oldCharset = oldMapping.get(changedFile);
if (!Comparing.equal(newCharset, oldCharset)) {
reloadDir(changedFile, newCharset);
reloadDir(changedFile, reloadProcessor);
}
}
}
@@ -302,10 +300,29 @@ public class EncodingProjectManagerImpl extends EncodingProjectManager {
myModificationCount++;
}
private void reloadDir(VirtualFile file, final Charset newCharset) {
private static Processor<VirtualFile> createChangeCharsetProcessor() {
return new Processor<VirtualFile>() {
@Override
public boolean process(final VirtualFile file) {
if (!(file instanceof VirtualFileSystemEntry)) return false;
Document cachedDocument = FileDocumentManager.getInstance().getCachedDocument(file);
if (cachedDocument == null) return true;
ProgressManager.progress("Reloading files...", file.getPresentableUrl());
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
clearAndReload(file);
}
});
return true;
}
};
}
private void reloadDir(VirtualFile file, @NotNull final Processor<VirtualFile> processor) {
if (file == null) {
for (VirtualFile virtualFile : ProjectRootManager.getInstance(myProject).getContentRoots()) {
reloadDir(virtualFile, newCharset);
reloadDir(virtualFile, processor);
}
return;
}
@@ -313,20 +330,7 @@ public class EncodingProjectManagerImpl extends EncodingProjectManager {
VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull final VirtualFile file) {
if (!(file instanceof VirtualFileSystemEntry)) return false;
if (!file.isCharsetSet()) return true;
Charset oldCharset = file.getCharset();
if (!Comparing.equal(newCharset, oldCharset)) {
ProgressManager.progress("Reloading files...", file.getPresentableUrl());
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
setAndSaveOrReload(file, newCharset);
}
});
}
return true;
return processor.process(file);
}
});
}
@@ -346,7 +350,39 @@ public class EncodingProjectManagerImpl extends EncodingProjectManager {
@Override
public void setUseUTFGuessing(final VirtualFile virtualFile, final boolean useUTFGuessing) {
myUseUTFGuessing = useUTFGuessing;
if (myUseUTFGuessing != useUTFGuessing) {
myUseUTFGuessing = useUTFGuessing;
reloadAllFilesUnder(null);
}
}
private void reloadAllFilesUnder(final VirtualFile root) {
FileDocumentManager.getInstance().saveAllDocuments(); // consider all files as unmodified
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
reloadDir(root, new Processor<VirtualFile>() {
@Override
public boolean process(final VirtualFile file) {
if (!(file instanceof VirtualFileSystemEntry)) return true;
Document cachedDocument = FileDocumentManager.getInstance().getCachedDocument(file);
if (cachedDocument != null) {
ProgressManager.progress("Reloading file...", file.getPresentableUrl());
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
reload(file);
}
});
}
else if (file.isCharsetSet()) {
file.setCharset(null);
}
return true;
}
});
}
}, "Reload Files", false, myProject);
}
@Override

View File

@@ -18,10 +18,8 @@ package com.intellij.openapi.vfs.encoding;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.impl.status.EncodingActionsPair;
import com.intellij.pom.Navigatable;
@@ -55,9 +53,7 @@ public class FileChangeEncodingGroup extends DefaultActionGroup implements DumbA
virtualFile = null;
}
Editor editor = e.getData(PlatformDataKeys.EDITOR);
boolean enabled =
encodingActionsPair.areActionsEnabled(null, editor, editor == null ? null : editor.getComponent(), virtualFile, getEventProject(e));
boolean enabled = virtualFile != null && EncodingActionsPair.checkSomeActionEnabled(virtualFile) == null;
removeAll();
if (enabled) {
addAll(encodingActionsPair.createActionGroup());

View File

@@ -32,7 +32,6 @@ import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
@@ -40,6 +39,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileEvent;
import com.intellij.openapi.vfs.VirtualFileListener;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -54,14 +54,15 @@ import java.util.Arrays;
* @author cdr
*/
public class ReloadFileInOtherEncodingAction extends AnAction implements DumbAware {
protected String text;
public ReloadFileInOtherEncodingAction() {
text = "Reload in...";
this("Reload in...");
}
protected ReloadFileInOtherEncodingAction(String text) {
super(text);
}
@Nullable("null means disabled, otherwise it's the document and the action description")
protected Pair<Document, String> checkEnabled(@NotNull VirtualFile virtualFile) {
public Pair<Document, String> checkEnabled(@NotNull VirtualFile virtualFile) {
String failReason = ChooseFileEncodingAction.checkCanReload(virtualFile).second;
if (failReason != null) return null;
FileDocumentManager documentManager = FileDocumentManager.getInstance();
@@ -82,7 +83,6 @@ public class ReloadFileInOtherEncodingAction extends AnAction implements DumbAwa
e.getPresentation().setEnabled(pair != null);
if (pair != null) {
e.getPresentation().setDescription(pair.second);
e.getPresentation().setText(text);
}
}
@@ -112,9 +112,9 @@ public class ReloadFileInOtherEncodingAction extends AnAction implements DumbAwa
@NotNull
@Override
protected DefaultActionGroup createPopupActionGroup(JComponent button) {
return createGroup(null, "Reload file ''{0}'' in''{1}''", myFile.getCharset(), new Condition<Charset>() {
return createGroup(null, myFile.getCharset(), new Function<Charset, String>() {
@Override
public boolean value(Charset charset) {
public String fun(Charset charset) {
return isCompatibleCharset(myFile, bytes, document.getText(), charset);
}
}); // no 'clear'
@@ -129,8 +129,8 @@ public class ReloadFileInOtherEncodingAction extends AnAction implements DumbAwa
}
.createPopupActionGroup(null);
final ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(
text, group, e.getDataContext(), JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false);
final ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(getTemplatePresentation().getText(),
group, e.getDataContext(), JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false);
popup.showInBestPositionFor(e.getDataContext());
}
@@ -176,8 +176,8 @@ public class ReloadFileInOtherEncodingAction extends AnAction implements DumbAwa
@NotNull String text,
@NotNull Charset charset,
@NotNull String action) {
if (!isCompatibleCharset(virtualFile, bytes, text, charset)) {
int res = Messages.showDialog("Encoding '" + charset.displayName() + "' does not support some characters from the text.",
if (isCompatibleCharset(virtualFile, bytes, text, charset) == null) {
int res = Messages.showDialog("File '"+virtualFile.getName()+"' most likely wasn't stored in the '" + charset.displayName() + "' encoding.",
"Incompatible Encoding: " + charset.displayName(), new String[]{action + " anyway", "Cancel"}, 1,
AllIcons.General.WarningDialog);
if (res != 0) return false;
@@ -209,7 +209,8 @@ public class ReloadFileInOtherEncodingAction extends AnAction implements DumbAwa
}
// charset filter
public boolean isCompatibleCharset(@NotNull VirtualFile virtualFile, @NotNull byte[] bytesOnDisk, @NotNull String text, @NotNull Charset charset) {
return canBeLoadedIn(virtualFile, bytesOnDisk, charset);
@Nullable("null means incompatible")
public String isCompatibleCharset(@NotNull VirtualFile virtualFile, @NotNull byte[] bytesOnDisk, @NotNull String text, @NotNull Charset charset) {
return canBeLoadedIn(virtualFile, bytesOnDisk, charset) ? "Reload file '" + virtualFile.getName() + "' in '" + charset + "'" : null;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* 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.
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.vfs.encoding;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
@@ -25,8 +26,6 @@ import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileEvent;
import com.intellij.openapi.vfs.VirtualFileListener;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,9 +37,9 @@ import java.text.MessageFormat;
/**
* @author cdr
*/
public class ConvertFileEncodingAction extends ReloadFileInOtherEncodingAction {
public ConvertFileEncodingAction() {
text = "Convert to...";
public class SaveFileInEncodingAction extends ReloadFileInOtherEncodingAction {
public SaveFileInEncodingAction() {
super("Save in...");
}
@Nullable
@@ -61,18 +60,24 @@ public class ConvertFileEncodingAction extends ReloadFileInOtherEncodingAction {
}
@Override
public boolean isCompatibleCharset(@NotNull VirtualFile virtualFile, @NotNull byte[] bytesOnDisk, @NotNull String text, @NotNull Charset charset) {
return canConvertTo(virtualFile, text, charset);
@Nullable("null means incompatible")
public String isCompatibleCharset(@NotNull VirtualFile virtualFile, @NotNull byte[] bytesOnDisk, @NotNull String text, @NotNull Charset charset) {
return isSafeToConvertTo(virtualFile, text, charset) ? "Convert and save file '" + virtualFile.getName() + "' in '" + charset + "'" : null;
}
private static boolean canConvertTo(@NotNull VirtualFile virtualFile, @NotNull String text, @NotNull Charset charset) {
Pair<Charset, byte[]> chosen = LoadTextUtil.chooseMostlyHarmlessCharset(virtualFile.getCharset(), charset, text);
private static boolean isSafeToConvertTo(@NotNull VirtualFile virtualFile, @NotNull String text, @NotNull Charset charset) {
try {
Pair<Charset, byte[]> chosen = LoadTextUtil.chooseMostlyHarmlessCharset(virtualFile.getCharset(), charset, text);
byte[] buffer = chosen.second;
byte[] buffer = chosen.second;
CharSequence textLoadedBack = LoadTextUtil.getTextByBinaryPresentation(buffer, charset);
CharSequence textLoadedBack = LoadTextUtil.getTextByBinaryPresentation(buffer, charset);
return text.equals(textLoadedBack.toString());
return text.equals(textLoadedBack.toString());
}
catch (UnsupportedOperationException e) { // unsupported encoding
return false;
}
}
@Override
@@ -82,43 +87,48 @@ public class ConvertFileEncodingAction extends ReloadFileInOtherEncodingAction {
@NotNull byte[] bytes,
@NotNull final Charset charset) {
if (!checkCompatibleEncodingAndWarn(virtualFile, bytes, document.getText(), charset, "Convert")) return;
convert(document, editor, virtualFile, charset);
saveIn(document, editor, virtualFile, charset);
}
public static void convert(@NotNull Document document, Editor editor, @NotNull VirtualFile virtualFile, @NotNull Charset charset) {
@Override
protected boolean checkCompatibleEncodingAndWarn(@NotNull VirtualFile virtualFile,
@NotNull byte[] bytes,
@NotNull String text,
@NotNull Charset charset,
@NotNull String action) {
if (isCompatibleCharset(virtualFile, bytes, text, charset) == null) {
int res = Messages.showDialog("Encoding '" + charset.displayName() + "' does not support some characters from the text.",
"Incompatible Encoding: " + charset.displayName(), new String[]{action + " anyway", "Cancel"}, 1,
AllIcons.General.WarningDialog);
if (res != 0) return false;
}
return true;
}
public static void saveIn(@NotNull Document document, Editor editor, @NotNull VirtualFile virtualFile, @NotNull Charset charset) {
FileDocumentManager documentManager = FileDocumentManager.getInstance();
if (documentManager.isFileModified(virtualFile)) {
EncodingManager.getInstance().setEncoding(virtualFile, charset);
LoadTextUtil.setCharsetWasDetectedFromBytes(virtualFile, null);
documentManager.saveDocument(document);
documentManager.saveDocument(document);
Project project = ProjectLocator.getInstance().guessProjectForFile(virtualFile);
boolean writable = project == null ? virtualFile.isWritable() : ReadonlyStatusHandler.ensureFilesWritable(project, virtualFile);
if (!writable) {
CommonRefactoringUtil
.showErrorHint(project, editor, "Cannot save the file " + virtualFile.getPresentableUrl(), "Unable to Save", null);
return;
}
else {
Project project = ProjectLocator.getInstance().guessProjectForFile(virtualFile);
boolean writable = project == null ? virtualFile.isWritable() : ReadonlyStatusHandler.ensureFilesWritable(project, virtualFile);
if (!writable) {
CommonRefactoringUtil
.showErrorHint(project, editor, "Cannot save the file " + virtualFile.getPresentableUrl(), "Unable to Save", null);
return;
}
virtualFile.setCharset(charset);
try {
LoadTextUtil.write(project, virtualFile, virtualFile, document.getText(), document.getModificationStamp());
}
catch (IOException io) {
Messages.showErrorDialog(project, io.getMessage(), "Error Writing File");
}
EncodingManager.getInstance().setEncoding(virtualFile, charset);
((VirtualFileListener)documentManager).contentsChanged(new VirtualFileEvent(null, virtualFile, virtualFile.getName(), virtualFile.getParent()));
virtualFile.setCharset(charset);
try {
LoadTextUtil.write(project, virtualFile, virtualFile, document.getText(), document.getModificationStamp());
}
catch (IOException io) {
Messages.showErrorDialog(project, io.getMessage(), "Error Writing File");
}
EncodingManager.getInstance().setEncoding(virtualFile, charset);
}
@Nullable("null means enabled, notnull means disabled and contains error message")
private static String checkCanConvert(@NotNull VirtualFile virtualFile) {
public static String checkCanConvert(@NotNull VirtualFile virtualFile) {
if (virtualFile.isDirectory()) {
return "file is a directory";
}

View File

@@ -15,42 +15,27 @@
*/
package com.intellij.openapi.wm.impl.status;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.encoding.ConvertFileEncodingAction;
import com.intellij.openapi.vfs.encoding.ChooseFileEncodingAction;
import com.intellij.openapi.vfs.encoding.ReloadFileInOtherEncodingAction;
import com.intellij.openapi.vfs.encoding.SaveFileInEncodingAction;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.awt.event.InputEvent;
public class EncodingActionsPair {
private final ConvertFileEncodingAction convert = new ConvertFileEncodingAction();
private final SaveFileInEncodingAction save = new SaveFileInEncodingAction();
private final ReloadFileInOtherEncodingAction reload = new ReloadFileInOtherEncodingAction();
public boolean areActionsEnabled(InputEvent e,Editor editor, Component component, VirtualFile selectedFile, Project project) {
DataContext dataContext = createDataContext(editor, component, selectedFile, project);
convert.update(new AnActionEvent(e, dataContext, "", convert.getTemplatePresentation(), ActionManager.getInstance(), 0));
reload.update(new AnActionEvent(e, dataContext, "", reload.getTemplatePresentation(), ActionManager.getInstance(), 0));
return convert.getTemplatePresentation().isEnabled() || reload.getTemplatePresentation().isEnabled();
}
@NotNull
public static DataContext createDataContext(Editor editor, Component component, VirtualFile selectedFile, Project project) {
DataContext parent = DataManager.getInstance().getDataContext(component);
return SimpleDataContext.getSimpleContext(PlatformDataKeys.VIRTUAL_FILE.getName(), selectedFile,
SimpleDataContext.getSimpleContext(PlatformDataKeys.PROJECT.getName(), project,
SimpleDataContext.getSimpleContext(PlatformDataKeys.CONTEXT_COMPONENT.getName(), editor == null ? null : editor.getComponent(),
parent)));
// null means enabled, error description otherwise
public static String checkSomeActionEnabled(@NotNull VirtualFile selectedFile) {
String saveError = SaveFileInEncodingAction.checkCanConvert(selectedFile);
String reloadError = ChooseFileEncodingAction.checkCanReload(selectedFile).second;
return saveError == null ? reloadError : saveError;
}
public DefaultActionGroup createActionGroup() {
DefaultActionGroup group = new DefaultActionGroup();
group.add(convert);
group.add(save);
group.add(reload);
return group;

View File

@@ -30,6 +30,7 @@ import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
@@ -207,14 +208,15 @@ public class EncodingPanel extends EditorBasedWidget implements StatusBarWidget.
@Override
public void run() {
VirtualFile file = getSelectedFile();
Charset charset = cachedCharsetFromContent(file);
String fromBytes = file == null ? null : LoadTextUtil.wasCharsetDetectedFromBytes(file);
Charset charset = fromBytes == null ? cachedCharsetFromContent(file) : null;
if (charset == null && file != null) charset = file.getCharset();
String text = charset == null ? "" : charset.displayName();
actionEnabled = encodingActionsPair.areActionsEnabled(null,getEditor(), (Component)myStatusBar, file, getProject());
String failReason = file == null ? null : EncodingActionsPair.checkSomeActionEnabled(file);
actionEnabled = file != null && failReason == null;
Pair<Charset,String> check = file == null ? null : ChooseFileEncodingAction.checkCanReload(file);
String failReason = check == null ? null : check.second;
String toolTip = "File Encoding" +
(check == null || check.first == null ? "" : ": "+check.first.displayName()) +
(failReason == null ? "" : actionEnabled ? " ("+failReason+")" : " (change disabled: " + failReason + ")");