[mod-commands] IJPL-157211 Support creation of binary files via ModCommand

GitOrigin-RevId: abebbf301426d4d66abeadc4b72afa8dfd041f0e
This commit is contained in:
Tagir Valeev
2024-06-26 17:31:23 +02:00
committed by intellij-monorepo-bot
parent 8fc6ebcd16
commit 2f10b5f14d
6 changed files with 84 additions and 10 deletions

View File

@@ -0,0 +1,28 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.modcommand;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.openapi.fileTypes.UserBinaryFileType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.LightPlatformCodeInsightTestCase;
import java.io.IOException;
public final class ModCommandTest extends LightPlatformCodeInsightTestCase {
public void testBinaryFile() throws IOException {
configureFromFileText("dummy.txt", "");
VirtualFile root = getSourceRoot();
FutureVirtualFile futureFile = new FutureVirtualFile(root, "test.dat", UserBinaryFileType.INSTANCE);
byte[] content = {'h', 'e', 'l', 'l', 'o'};
ModCreateFile command = new ModCreateFile(futureFile, new ModCreateFile.Binary(content));
ModCommandExecutor executor = ModCommandExecutor.getInstance();
ActionContext context = ActionContext.from(null, getFile());
IntentionPreviewInfo preview = executor.getPreview(command, context);
assertEquals(new IntentionPreviewInfo.CustomDiff(UserBinaryFileType.INSTANCE, "test.dat", "", "(binary content)", true), preview);
ModCommandExecutor.BatchExecutionResult result = executor.executeInBatch(context, command);
assertEquals(ModCommandExecutor.Result.SUCCESS, result);
VirtualFile child = root.findChild("test.dat");
assertNotNull(child);
assertOrderedEquals(content, child.contentsToByteArray());
}
}

View File

@@ -678,8 +678,10 @@ f:com.intellij.codeInsight.intention.preview.IntentionPreviewInfo$CustomDiff
- <init>(com.intellij.openapi.fileTypes.FileType,java.lang.String,java.lang.String):V
- <init>(com.intellij.openapi.fileTypes.FileType,java.lang.String,java.lang.String,java.lang.String):V
- <init>(com.intellij.openapi.fileTypes.FileType,java.lang.String,java.lang.String,java.lang.String,Z):V
- equals(java.lang.Object):Z
- fileName():java.lang.String
- fileType():com.intellij.openapi.fileTypes.FileType
- hashCode():I
- modifiedText():java.lang.String
- originalText():java.lang.String
- showLineNumbers():Z
@@ -2683,10 +2685,25 @@ f:com.intellij.modcommand.ModCopyToClipboard
f:com.intellij.modcommand.ModCreateFile
- java.lang.Record
- com.intellij.modcommand.ModCommand
- <init>(com.intellij.modcommand.FutureVirtualFile,java.lang.String):V
- <init>(com.intellij.modcommand.FutureVirtualFile,com.intellij.modcommand.ModCreateFile$Content):V
- content():com.intellij.modcommand.ModCreateFile$Content
- f:equals(java.lang.Object):Z
- file():com.intellij.modcommand.FutureVirtualFile
- f:hashCode():I
f:com.intellij.modcommand.ModCreateFile$Binary
- java.lang.Record
- com.intellij.modcommand.ModCreateFile$Content
- <init>(B[]):V
- bytes():B[]
- f:equals(java.lang.Object):Z
- f:hashCode():I
com.intellij.modcommand.ModCreateFile$Content
f:com.intellij.modcommand.ModCreateFile$Text
- java.lang.Record
- com.intellij.modcommand.ModCreateFile$Content
- <init>(java.lang.String):V
- f:equals(java.lang.Object):Z
- f:hashCode():I
- text():java.lang.String
f:com.intellij.modcommand.ModDeleteFile
- java.lang.Record

View File

@@ -247,3 +247,4 @@ modcommand.result.conflict=There are conflicts; user interaction required
modcommand.executor.error.files.are.marked.as.readonly=Files are marked as read-only
modcommand.executor.unable.to.find.the.new.file=Unable to find the new file {0}
modcommand.executor.modification.of.guarded.region=Action tries to modify guarded region
preview.binary.content=(binary content)

View File

@@ -7,5 +7,24 @@ import org.jetbrains.annotations.NotNull;
* @param file file to create
* @param text file content
*/
public record ModCreateFile(@NotNull FutureVirtualFile file, @NotNull String text) implements ModCommand {
public record ModCreateFile(@NotNull FutureVirtualFile file, @NotNull Content content) implements ModCommand {
/**
* New file content
*/
public sealed interface Content {}
/**
* Text content
*
* @param text text to write into the new file. The encoding will be selected automatically based on the IDE settings
*/
public record Text(@NotNull String text) implements Content {}
/**
* Binary content
*
* @param bytes bytes to write into the new file.
*/
public record Binary(byte @NotNull [] bytes) implements Content {}
}

View File

@@ -155,11 +155,16 @@ public class ModCommandBatchExecutorImpl implements ModCommandExecutor {
try {
return WriteAction.compute(() -> {
VirtualFile newFile = parent.createChildData(this, file.getName());
PsiFile psiFile = PsiManager.getInstance(project).findFile(newFile);
if (psiFile == null) return AnalysisBundle.message("modcommand.executor.unable.to.find.the.new.file", file.getName());
Document document = psiFile.getViewProvider().getDocument();
document.setText(create.text());
PsiDocumentManager.getInstance(project).commitDocument(document);
if (create.content() instanceof ModCreateFile.Text text) {
PsiFile psiFile = PsiManager.getInstance(project).findFile(newFile);
if (psiFile == null) return AnalysisBundle.message("modcommand.executor.unable.to.find.the.new.file", file.getName());
Document document = psiFile.getViewProvider().getDocument();
document.setText(text.text());
PsiDocumentManager.getInstance(project).commitDocument(document);
}
else if (create.content() instanceof ModCreateFile.Binary binary) {
newFile.setBinaryContent(binary.bytes());
}
return null;
});
}
@@ -251,10 +256,12 @@ public class ModCommandBatchExecutorImpl implements ModCommandExecutor {
}
else if (command instanceof ModCreateFile createFile) {
VirtualFile vFile = createFile.file();
String content =
createFile.content() instanceof ModCreateFile.Text text ? text.text() : AnalysisBundle.message("preview.binary.content");
customDiffList.add(new IntentionPreviewInfo.CustomDiff(vFile.getFileType(),
getFileNamePresentation(project, vFile),
"",
createFile.text(),
content,
true));
}
else if (command instanceof ModNavigate navigate && navigate.caret() != -1) {
@@ -275,7 +282,8 @@ public class ModCommandBatchExecutorImpl implements ModCommandExecutor {
else if (command instanceof ModDisplayMessage message) {
if (message.kind() == ModDisplayMessage.MessageKind.ERROR) {
return new IntentionPreviewInfo.Html(new HtmlBuilder().append(
AnalysisBundle.message("preview.cannot.perform.action")).br().append(message.messageText()).toFragment(), IntentionPreviewInfo.InfoKind.ERROR);
AnalysisBundle.message("preview.cannot.perform.action")).br().append(message.messageText()).toFragment(),
IntentionPreviewInfo.InfoKind.ERROR);
}
else if (navigateInfo == IntentionPreviewInfo.EMPTY) {
navigateInfo = new IntentionPreviewInfo.Html(message.messageText());

View File

@@ -281,7 +281,8 @@ final class PsiUpdateImpl {
documentManager.commitDocument(document);
documentManager.doPostponedOperationsAndUnblockDocument(document);
return new ModCreateFile(new FutureVirtualFile(directory().getOriginalFile(),
vf.getName(), vf.getFileType()), psiFile.getText());
vf.getName(), vf.getFileType()),
new ModCreateFile.Text(psiFile.getText()));
});
}
}