PY-75877 Fix problem with jupyter notebook shelve

(cherry picked from commit 9172e753ec6e1dffca73aa9bf4608400f1ea3c9a)

GitOrigin-RevId: 6331ca59f530bb4b7ed177639ef2f1cafab3ff32
This commit is contained in:
Andrey.Matveev
2025-06-03 11:01:33 +03:00
committed by intellij-monorepo-bot
parent fd6c1512cd
commit 57971b4179
6 changed files with 91 additions and 16 deletions

View File

@@ -6,7 +6,27 @@ import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
interface TextPresentationTransformer {
fun fromPersistent(text: CharSequence, virtualFile: VirtualFile):CharSequence
/**
* Transforms the given text from its persistent representation to its in-memory representation
* based on the specified virtual file's context.
*
* Used in Jupyter Notebooks to switch from JSON representation to source code text with #%% separators between cells.
*
* @param text The text in its persistent representation to be transformed
* @param virtualFile The virtual file providing context for the transformation
* @return The text in its in-memory representation
*/
fun fromPersistent(text: CharSequence, virtualFile: VirtualFile): CharSequence
fun toPersistent(text: CharSequence, virtualFile: VirtualFile):CharSequence
/**
* Transforms the given text from its in-memory representation to its persistent representation
* based on the context provided by the specified virtual file.
*
* Used in Jupyter Notebooks to switch from source code text with #%% separators between cells to JSON representation.
*
* @param text The text in its in-memory representation to be transformed
* @param virtualFile The virtual file providing context for the transformation
* @return The text in its persistent representation
*/
fun toPersistent(text: CharSequence, virtualFile: VirtualFile): CharSequence
}

View File

@@ -34,6 +34,9 @@ class TextPresentationTransformers : FileTypeExtension<TextPresentationTransform
companion object {
val EP: ExtensionPointName<KeyedLazyInstance<TextPresentationTransformer>> = ExtensionPointName("com.intellij.fileEditor.textPresentationTransformer")
/**
* See [TextPresentationTransformer.fromPersistent]
*/
@JvmStatic
fun fromPersistent(text: CharSequence, virtualFile: VirtualFile): CharSequence {
val transformer = service<TextPresentationTransformers>().forFileType(virtualFile.fileType)
@@ -44,6 +47,9 @@ class TextPresentationTransformers : FileTypeExtension<TextPresentationTransform
return transformer.fromPersistent(text, virtualFile)
}
/**
* See [TextPresentationTransformer.toPersistent]
*/
@JvmStatic
fun toPersistent(text: CharSequence, virtualFile: VirtualFile): CharSequence {
val transformer = service<TextPresentationTransformers>().forFileType(virtualFile.fileType)

View File

@@ -408,16 +408,17 @@ public final class LoadTextUtil {
write(project, file, requestor, newText, -1);
}
/**
* Normally, one should not use this method.
*/
@ApiStatus.Internal
public static void write(@Nullable Project project,
@NotNull VirtualFile virtualFile,
@NotNull Object requestor,
@NotNull String text,
long newModificationStamp) throws IOException {
long newModificationStamp,
boolean applyTextTransformer) throws IOException {
Charset existing = virtualFile.getCharset();
text = TextPresentationTransformers.toPersistent(text, virtualFile).toString();
if (applyTextTransformer) {
text = TextPresentationTransformers.toPersistent(text, virtualFile).toString();
}
Pair.NonNull<Charset, byte[]> chosen = charsetForWriting(project, virtualFile, text, existing);
Charset charset = chosen.first;
byte[] buffer = chosen.second;
@@ -433,6 +434,17 @@ public final class LoadTextUtil {
}
}
/**
* Normally, one should not use this method.
*/
public static void write(@Nullable Project project,
@NotNull VirtualFile virtualFile,
@NotNull Object requestor,
@NotNull String text,
long newModificationStamp) throws IOException {
write(project, virtualFile, requestor, text, newModificationStamp, true);
}
public static @NotNull Pair.NonNull<Charset, byte[]> charsetForWriting(@Nullable Project project,
@NotNull VirtualFile virtualFile,
@NotNull String text,

View File

@@ -32,6 +32,7 @@ import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.encoding.EncodingManager;
import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
import com.intellij.openapi.vfs.transformer.TextPresentationTransformers;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.testFramework.BinaryLightVirtualFile;
import com.intellij.testFramework.LightVirtualFile;
@@ -394,7 +395,10 @@ public final class DiffContentFactoryImpl extends DiffContentFactoryEx {
if (project == null || project.isDefault()) return null;
if (fileType != null && fileType.isBinary()) return null;
LightVirtualFile file = new MyLightVirtualFile(lightFilePath, fileType, content);
// Here we need a dummy originalFile to pass it to [TextPresentationTransformers.fromPersistent]
LightVirtualFile originalFile = new MyLightVirtualFile(lightFilePath, fileType, content);
String convertedText = TextPresentationTransformers.fromPersistent(content, originalFile).toString();
LightVirtualFile file = new MyLightVirtualFile(lightFilePath, fileType, convertedText);
file.setWritable(!readOnly);
return ReadAction.compute(() -> {
@@ -402,6 +406,7 @@ public final class DiffContentFactoryImpl extends DiffContentFactoryEx {
if (document == null) {
return null;
}
PsiDocumentManager.getInstance(project).getPsiFile(document);
return document;
});
@@ -884,8 +889,10 @@ public final class DiffContentFactoryImpl extends DiffContentFactoryEx {
@Override
public @Nullable FileType guessContentType() {
VirtualFile file = myFilePath.getVirtualFile();
if (file != null) return file.getFileType();
return null;
if (file == null) {
return FileTypeManager.getInstance().getFileTypeByFileName(myFilePath.getName());
}
return file.getFileType();
}
}

View File

@@ -2,12 +2,14 @@
package com.intellij.openapi.vcs.changes;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.transformer.TextPresentationTransformers;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -28,7 +30,15 @@ public class CurrentContentRevision implements ByteBackedContentRevision {
return null;
}
Document doc = ReadAction.compute(() -> FileDocumentManager.getInstance().getDocument(vFile));
return doc == null ? null : doc.getText();
if (doc == null) {
return null;
}
else {
// In some cases like Jupyter Notebooks we need to make TextPresentationTransformers.toPersistent to have a correct text representation
// In the case of Jupyter Notebooks it is a JSON representation of the notebook instead of representation with #%% cells separators
String docText = doc.getText();
return ReadAction.compute(() -> TextPresentationTransformers.toPersistent(docText, vFile).toString());
}
}
@Override

View File

@@ -7,11 +7,13 @@ import com.intellij.openapi.diff.impl.patch.CharsetEP;
import com.intellij.openapi.diff.impl.patch.TextFilePatch;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.changes.CommitContext;
import com.intellij.openapi.vcs.changes.patch.ApplyPatchForBaseRevisionTexts;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.transformer.TextPresentationTransformers;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,15 +38,17 @@ public final class ApplyTextFilePatch extends ApplyFilePatchBase<TextFilePatch>
throw new IOException("Failed to set contents for updated file " + fileToPatch.getPath());
}
GenericPatchApplier.AppliedPatch appliedPatch = GenericPatchApplier.apply(document.getText(), myPatch.getHunks());
String documentText = document.getText();
String fileTextPersistent = TextPresentationTransformers.toPersistent(documentText, fileToPatch).toString();
GenericPatchApplier.AppliedPatch appliedPatch = GenericPatchApplier.apply(fileTextPersistent, myPatch.getHunks());
if (appliedPatch != null) {
if (appliedPatch.status == ApplyPatchStatus.ALREADY_APPLIED) {
return new Result(appliedPatch.status);
}
if (appliedPatch.status == ApplyPatchStatus.SUCCESS) {
VcsFacade.getInstance().runHeavyModificationTask(project, document, () -> document.setText(appliedPatch.patchedText));
FileDocumentManager.getInstance().saveDocument(document);
updateDocumentContent(project, document, fileToPatch, appliedPatch.patchedText);
return new Result(appliedPatch.status);
}
}
@@ -77,7 +81,23 @@ public final class ApplyTextFilePatch extends ApplyFilePatchBase<TextFilePatch>
catch (IllegalArgumentException ignore) {
}
}
document.setText(myPatch.getSingleHunkPatchText());
FileDocumentManager.getInstance().saveDocument(document);
String patchText = myPatch.getSingleHunkPatchText();
updateDocumentContent(project, document, newFile, patchText);
}
private void updateDocumentContent(@NotNull Project project,
@NotNull Document document,
@NotNull VirtualFile file,
@NotNull String patchedText) {
VcsFacade.getInstance().runHeavyModificationTask(project, document, () -> {
try {
LoadTextUtil.write(project, file, this, patchedText, -1, false);
}
catch (IOException e) {
throw new RuntimeException(e);
}
FileDocumentManager.getInstance().reloadFromDisk(document);
});
}
}