mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
diff: support BOM differences
This commit is contained in:
@@ -59,6 +59,12 @@ public interface DocumentContent extends DiffContent {
|
||||
@Nullable
|
||||
default Charset getCharset() { return null; }
|
||||
|
||||
/**
|
||||
* @return original file byte order mark
|
||||
*/
|
||||
@Nullable
|
||||
default Boolean getBOM() { return null; }
|
||||
|
||||
@Nullable
|
||||
@Deprecated
|
||||
default OpenFileDescriptor getOpenFileDescriptor(int offset) {
|
||||
|
||||
@@ -94,7 +94,7 @@ public class DiffContentFactoryImpl extends DiffContentFactory {
|
||||
@Override
|
||||
public DocumentContent create(@NotNull Document document, @Nullable DocumentContent referent) {
|
||||
if (referent == null) return new DocumentContentImpl(document);
|
||||
return new DocumentContentImpl(document, referent.getContentType(), referent.getHighlightFile(), null, null);
|
||||
return new DocumentContentImpl(document, referent.getContentType(), referent.getHighlightFile(), null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,7 +107,7 @@ public class DiffContentFactoryImpl extends DiffContentFactory {
|
||||
@NotNull
|
||||
public DocumentContent create(@Nullable Project project, @NotNull Document document, @Nullable FileType fileType) {
|
||||
VirtualFile file = FileDocumentManager.getInstance().getFile(document);
|
||||
if (file == null) return new DocumentContentImpl(document, fileType, null, null, null);
|
||||
if (file == null) return new DocumentContentImpl(document, fileType, null, null, null, null);
|
||||
return create(project, document, file);
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ public class DiffContentFactoryImpl extends DiffContentFactory {
|
||||
LineSeparator separator = respectLineSeparators ? StringUtil.detectSeparators(text) : null;
|
||||
Document document = EditorFactory.getInstance().createDocument(StringUtil.convertLineSeparators(text));
|
||||
if (readOnly) document.setReadOnly(true);
|
||||
return new DocumentContentImpl(document, type, highlightFile, separator, charset);
|
||||
return new DocumentContentImpl(document, type, highlightFile, separator, charset, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -37,21 +37,24 @@ public class DocumentContentImpl extends DiffContentBase implements DocumentCont
|
||||
|
||||
@Nullable private final LineSeparator mySeparator;
|
||||
@Nullable private final Charset myCharset;
|
||||
@Nullable private final Boolean myBOM;
|
||||
|
||||
public DocumentContentImpl(@NotNull Document document) {
|
||||
this(document, null, null, null, null);
|
||||
this(document, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public DocumentContentImpl(@NotNull Document document,
|
||||
@Nullable FileType type,
|
||||
@Nullable VirtualFile highlightFile,
|
||||
@Nullable LineSeparator separator,
|
||||
@Nullable Charset charset) {
|
||||
@Nullable Charset charset,
|
||||
@Nullable Boolean bom) {
|
||||
myDocument = document;
|
||||
myType = type;
|
||||
myHighlightFile = highlightFile;
|
||||
mySeparator = separator;
|
||||
myCharset = charset;
|
||||
myBOM = bom;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -84,6 +87,12 @@ public class DocumentContentImpl extends DiffContentBase implements DocumentCont
|
||||
return mySeparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Boolean getBOM() {
|
||||
return myBOM;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FileType getContentType() {
|
||||
|
||||
@@ -40,8 +40,9 @@ public class FileAwareDocumentContent extends DocumentContentImpl {
|
||||
@Nullable FileType fileType,
|
||||
@Nullable VirtualFile highlightFile,
|
||||
@Nullable LineSeparator separator,
|
||||
@Nullable Charset charset) {
|
||||
super(document, fileType, highlightFile, separator, charset);
|
||||
@Nullable Charset charset,
|
||||
@Nullable Boolean bom) {
|
||||
super(document, fileType, highlightFile, separator, charset, bom);
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@@ -78,6 +79,7 @@ public class FileAwareDocumentContent extends DocumentContentImpl {
|
||||
private VirtualFile myHighlightFile;
|
||||
private LineSeparator mySeparator;
|
||||
private Charset myCharset;
|
||||
private Boolean myBOM;
|
||||
private Charset mySuggestedCharset;
|
||||
private boolean myMalformedContent;
|
||||
private String myFileName;
|
||||
@@ -140,6 +142,15 @@ public class FileAwareDocumentContent extends DocumentContentImpl {
|
||||
private Builder create(@NotNull byte[] content) {
|
||||
assert mySuggestedCharset != null;
|
||||
|
||||
Charset charset = CharsetToolkit.guessFromBOM(content);
|
||||
if (charset != null) {
|
||||
mySuggestedCharset = charset;
|
||||
myBOM = true;
|
||||
}
|
||||
else {
|
||||
myBOM = false;
|
||||
}
|
||||
|
||||
myCharset = mySuggestedCharset;
|
||||
try {
|
||||
String text = CharsetToolkit.tryDecodeString(content, mySuggestedCharset);
|
||||
@@ -165,7 +176,7 @@ public class FileAwareDocumentContent extends DocumentContentImpl {
|
||||
public FileAwareDocumentContent build() {
|
||||
if (FileTypes.UNKNOWN.equals(myFileType)) myFileType = PlainTextFileType.INSTANCE;
|
||||
FileAwareDocumentContent content
|
||||
= new FileAwareDocumentContent(myProject, myDocument, myFileType, myHighlightFile, mySeparator, myCharset);
|
||||
= new FileAwareDocumentContent(myProject, myDocument, myFileType, myHighlightFile, mySeparator, myCharset, myBOM);
|
||||
DiffUtil.addNotification(createNotification(), content);
|
||||
content.putUserData(DiffUserDataKeysEx.FILE_NAME, myFileName);
|
||||
return content;
|
||||
|
||||
@@ -35,7 +35,7 @@ public class FileDocumentContentImpl extends DocumentContentImpl implements File
|
||||
public FileDocumentContentImpl(@Nullable Project project,
|
||||
@NotNull Document document,
|
||||
@NotNull VirtualFile file) {
|
||||
super(document, file.getFileType(), file, getSeparator(file), file.getCharset());
|
||||
super(document, file.getFileType(), file, getSeparator(file), file.getCharset(), file.getBOM() != null);
|
||||
myProject = project;
|
||||
myFile = file;
|
||||
}
|
||||
|
||||
@@ -36,8 +36,10 @@ import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileWithoutContent;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.LineSeparator;
|
||||
import com.intellij.util.PathUtil;
|
||||
import com.intellij.util.TimeoutUtil;
|
||||
@@ -115,6 +117,9 @@ public class ExternalDiffToolUtil {
|
||||
Charset charset = content.getCharset();
|
||||
if (charset == null) charset = Charset.defaultCharset();
|
||||
|
||||
Boolean hasBom = content.getBOM();
|
||||
if (hasBom == null) hasBom = CharsetToolkit.getMandatoryBom(charset) != null;
|
||||
|
||||
String contentData = ReadAction.compute(() -> {
|
||||
return content.getDocument().getText();
|
||||
});
|
||||
@@ -123,6 +128,12 @@ public class ExternalDiffToolUtil {
|
||||
}
|
||||
|
||||
byte[] bytes = contentData.getBytes(charset);
|
||||
|
||||
byte[] bom = hasBom ? CharsetToolkit.getBom(charset) : null;
|
||||
if (bom != null) {
|
||||
bytes = ArrayUtil.mergeArrays(bom, bytes);
|
||||
}
|
||||
|
||||
return createFile(bytes, fileName);
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,9 @@ public class TextDiffViewerUtil {
|
||||
}
|
||||
|
||||
public static boolean areEqualCharsets(@NotNull List<? extends DiffContent> contents) {
|
||||
return areEqualDocumentContentProperties(contents, DocumentContent::getCharset);
|
||||
boolean sameCharset = areEqualDocumentContentProperties(contents, DocumentContent::getCharset);
|
||||
boolean sameBOM = areEqualDocumentContentProperties(contents, DocumentContent::getBOM);
|
||||
return sameCharset && sameBOM;
|
||||
}
|
||||
|
||||
private static <T> boolean areEqualDocumentContentProperties(@NotNull List<? extends DiffContent> contents,
|
||||
|
||||
@@ -467,23 +467,26 @@ public class DiffUtil {
|
||||
boolean equalSeparators,
|
||||
@Nullable Editor editor) {
|
||||
if (content instanceof EmptyContent) return null;
|
||||
DocumentContent documentContent = (DocumentContent)content;
|
||||
|
||||
Charset charset = equalCharsets ? null : ((DocumentContent)content).getCharset();
|
||||
LineSeparator separator = equalSeparators ? null : ((DocumentContent)content).getLineSeparator();
|
||||
Charset charset = equalCharsets ? null : documentContent.getCharset();
|
||||
Boolean bom = equalCharsets ? null : documentContent.getBOM();
|
||||
LineSeparator separator = equalSeparators ? null : documentContent.getLineSeparator();
|
||||
boolean isReadOnly = editor == null || editor.isViewer() || !canMakeWritable(editor.getDocument());
|
||||
|
||||
return createTitle(title, charset, separator, isReadOnly);
|
||||
return createTitle(title, separator, charset, bom, isReadOnly);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JComponent createTitle(@NotNull String title) {
|
||||
return createTitle(title, null, null, false);
|
||||
return createTitle(title, null, null, null, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JComponent createTitle(@NotNull String title,
|
||||
@Nullable Charset charset,
|
||||
@Nullable LineSeparator separator,
|
||||
@Nullable Charset charset,
|
||||
@Nullable Boolean bom,
|
||||
boolean readOnly) {
|
||||
if (readOnly) title += " " + DiffBundle.message("diff.content.read.only.content.title.suffix");
|
||||
|
||||
@@ -493,13 +496,13 @@ public class DiffUtil {
|
||||
if (charset != null && separator != null) {
|
||||
JPanel panel2 = new JPanel();
|
||||
panel2.setLayout(new BoxLayout(panel2, BoxLayout.X_AXIS));
|
||||
panel2.add(createCharsetPanel(charset));
|
||||
panel2.add(createCharsetPanel(charset, bom));
|
||||
panel2.add(Box.createRigidArea(new Dimension(4, 0)));
|
||||
panel2.add(createSeparatorPanel(separator));
|
||||
panel.add(panel2, BorderLayout.EAST);
|
||||
}
|
||||
else if (charset != null) {
|
||||
panel.add(createCharsetPanel(charset), BorderLayout.EAST);
|
||||
panel.add(createCharsetPanel(charset, bom), BorderLayout.EAST);
|
||||
}
|
||||
else if (separator != null) {
|
||||
panel.add(createSeparatorPanel(separator), BorderLayout.EAST);
|
||||
@@ -508,8 +511,13 @@ public class DiffUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JComponent createCharsetPanel(@NotNull Charset charset) {
|
||||
JLabel label = new JLabel(charset.displayName());
|
||||
private static JComponent createCharsetPanel(@NotNull Charset charset, @Nullable Boolean bom) {
|
||||
String text = charset.displayName();
|
||||
if (bom != null && bom) {
|
||||
text += " BOM";
|
||||
}
|
||||
|
||||
JLabel label = new JLabel(text);
|
||||
// TODO: specific colors for other charsets
|
||||
if (charset.equals(Charset.forName("UTF-8"))) {
|
||||
label.setForeground(JBColor.BLUE);
|
||||
|
||||
@@ -624,6 +624,12 @@ public class CharsetToolkit {
|
||||
return CHARSET_TO_MANDATORY_BOM.get(charset);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static byte[] getBom(@NotNull Charset charset) {
|
||||
if (charset.equals(UTF8_CHARSET)) return UTF8_BOM;
|
||||
return CHARSET_TO_MANDATORY_BOM.get(charset);
|
||||
}
|
||||
|
||||
// byte sequence for this encoding is allowed to be prepended with this BOM
|
||||
public static boolean canHaveBom(@NotNull Charset charset, @NotNull byte[] bom) {
|
||||
return charset.equals(UTF8_CHARSET) && Arrays.equals(bom, UTF8_BOM)
|
||||
|
||||
@@ -91,7 +91,7 @@ public class MigrateToNewDiffUtil {
|
||||
else {
|
||||
Document document = oldContent.getDocument();
|
||||
if (document == null) return null;
|
||||
return new DocumentContentImpl(document, oldContent.getContentType(), oldContent.getFile(), oldContent.getLineSeparator(), null) {
|
||||
return new DocumentContentImpl(document, oldContent.getContentType(), oldContent.getFile(), oldContent.getLineSeparator(), null, null) {
|
||||
@Nullable
|
||||
@Override
|
||||
public Navigatable getNavigatable(@NotNull LineCol position) {
|
||||
|
||||
Reference in New Issue
Block a user