mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[mod-command] Support file deletion right in PSI; fix committing new files; tests for PackageDotHtmlMayBePackageInfo fix
GitOrigin-RevId: 6736011022179d86709da329d57230f76abe64dd
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9dde90660e
commit
80fe6724fe
@@ -4,8 +4,14 @@ package com.intellij.modcommand;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @param file file to delete
|
||||
*/
|
||||
public record ModDeleteFile(@NotNull VirtualFile file) implements ModCommand {
|
||||
@Override
|
||||
public @NotNull Set<@NotNull VirtualFile> modifiedFiles() {
|
||||
return Set.of(file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@@ -33,13 +32,6 @@ public interface ModPsiUpdater {
|
||||
@Contract(value = "null -> null; !null -> !null")
|
||||
<E extends PsiElement> E getWritable(E e);
|
||||
|
||||
/**
|
||||
* Records a command to delete the particular file. Note that calling {@link PsiFile#delete()} may not
|
||||
* work, even if the file was obtained from {@link #getWritable(PsiElement)}.
|
||||
* @param file file to delete (can be physical or returned earlier by {@link #getWritable(PsiElement)}
|
||||
*/
|
||||
void deleteFile(@NotNull PsiFile file);
|
||||
|
||||
/**
|
||||
* Selects given element
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package com.intellij.psi.impl.file;
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeRegistry;
|
||||
import com.intellij.openapi.fileTypes.UnknownFileType;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.impl.PsiManagerImpl;
|
||||
@@ -15,8 +16,11 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class PsiFileImplUtil {
|
||||
private static final Key<Consumer<PsiFile>> NON_PHYSICAL_FILE_DELETE_HANDLER = Key.create("NonPhysicalFileDeleteHandler");
|
||||
|
||||
private PsiFileImplUtil() {
|
||||
}
|
||||
|
||||
@@ -33,6 +37,17 @@ public final class PsiFileImplUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public static void setNonPhysicalFileDeleteHandler(@NotNull PsiFile file, @NotNull Consumer<@NotNull PsiFile> handler) {
|
||||
if (file.isPhysical()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
file.putUserData(NON_PHYSICAL_FILE_DELETE_HANDLER, handler);
|
||||
}
|
||||
|
||||
public static boolean canDeleteNonPhysicalFile(@NotNull PsiFile file) {
|
||||
return !file.isPhysical() && file.getUserData(NON_PHYSICAL_FILE_DELETE_HANDLER) != null;
|
||||
}
|
||||
|
||||
public static PsiFile setName(@NotNull PsiFile file, @NotNull String newName) throws IncorrectOperationException {
|
||||
VirtualFile vFile = file.getViewProvider().getVirtualFile();
|
||||
@@ -61,6 +76,15 @@ public final class PsiFileImplUtil {
|
||||
}
|
||||
|
||||
public static void doDelete(@NotNull PsiFile file) throws IncorrectOperationException {
|
||||
if (!file.isPhysical()) {
|
||||
Consumer<PsiFile> handler = file.getUserData(NON_PHYSICAL_FILE_DELETE_HANDLER);
|
||||
if (handler == null) {
|
||||
throw new IncorrectOperationException();
|
||||
}
|
||||
handler.accept(file);
|
||||
return;
|
||||
}
|
||||
|
||||
PsiManagerImpl manager = (PsiManagerImpl)file.getManager();
|
||||
|
||||
VirtualFile vFile = file.getVirtualFile();
|
||||
|
||||
@@ -451,6 +451,7 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
|
||||
@Override
|
||||
public void checkDelete() throws IncorrectOperationException {
|
||||
if (!getViewProvider().isEventSystemEnabled()) {
|
||||
if (PsiFileImplUtil.canDeleteNonPhysicalFile(this)) return;
|
||||
throw new IncorrectOperationException();
|
||||
}
|
||||
CheckUtil.checkWritable(this);
|
||||
|
||||
@@ -157,7 +157,9 @@ public class ModCommandServiceImpl implements ModCommandService {
|
||||
VirtualFile newFile = parent.createChildData(this, file.getName());
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(newFile);
|
||||
if (psiFile == null) return false;
|
||||
psiFile.getViewProvider().getDocument().setText(create.text());
|
||||
Document document = psiFile.getViewProvider().getDocument();
|
||||
document.setText(create.text());
|
||||
PsiDocumentManager.getInstance(project).commitDocument(document);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.file.PsiDirectoryFactory;
|
||||
import com.intellij.psi.impl.file.PsiFileImplUtil;
|
||||
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
@@ -71,10 +72,12 @@ final class PsiUpdateImpl {
|
||||
private final @NotNull PsiFile myOrigFile;
|
||||
private final @NotNull PsiFile myCopyFile;
|
||||
private final @NotNull PsiDocumentManager myManager;
|
||||
private boolean myDeleted;
|
||||
|
||||
FileTracker(@NotNull PsiFile origFile) {
|
||||
myProject = origFile.getProject();
|
||||
myCopyFile = copyFile(myProject, origFile);
|
||||
PsiFileImplUtil.setNonPhysicalFileDeleteHandler(myCopyFile, f -> myDeleted = true);
|
||||
myDocument = myCopyFile.getViewProvider().getDocument();
|
||||
InjectedLanguageManager injectionManager = InjectedLanguageManager.getInstance(myProject);
|
||||
boolean injected = injectionManager.isInjectedFragment(origFile);
|
||||
@@ -108,10 +111,13 @@ final class PsiUpdateImpl {
|
||||
}
|
||||
|
||||
ModCommand getUpdateCommand() {
|
||||
myManager.commitDocument(myDocument);
|
||||
unblock();
|
||||
VirtualFile origVirtualFile = myOrigFile.getOriginalFile().getVirtualFile();
|
||||
if (origVirtualFile == null) return new ModNothing();
|
||||
if (myDeleted) {
|
||||
return new ModDeleteFile(origVirtualFile);
|
||||
}
|
||||
myManager.commitDocument(myDocument);
|
||||
unblock();
|
||||
String newText = myTargetFile.getText();
|
||||
return myOrigText.equals(newText) ? new ModNothing() :
|
||||
new ModUpdateFileText(origVirtualFile, myOrigText, newText, myFragments);
|
||||
@@ -201,7 +207,6 @@ final class PsiUpdateImpl {
|
||||
private final @NotNull FileTracker myTracker;
|
||||
private final @NotNull Map<PsiFile, FileTracker> myChangedFiles = new LinkedHashMap<>();
|
||||
private final @NotNull Map<VirtualFile, ModPsiUpdaterImpl.ChangedDirectoryInfo> myChangedDirectories = new LinkedHashMap<>();
|
||||
private final @NotNull Set<VirtualFile> myDeletedFiles = new LinkedHashSet<>();
|
||||
private @NotNull VirtualFile myNavigationFile;
|
||||
private int myCaretOffset;
|
||||
private @NotNull TextRange mySelection;
|
||||
@@ -220,10 +225,14 @@ final class PsiUpdateImpl {
|
||||
|
||||
@NotNull Stream<ModCommand> createFileCommands(@NotNull Project project) {
|
||||
PsiManager manager = PsiManager.getInstance(project);
|
||||
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
|
||||
return directory().getAddedChildren().values().stream()
|
||||
.map(vf -> {
|
||||
PsiFile psiFile = manager.findFile(vf);
|
||||
if (psiFile == null) return nop();
|
||||
Document document = psiFile.getViewProvider().getDocument();
|
||||
documentManager.commitDocument(document);
|
||||
documentManager.doPostponedOperationsAndUnblockDocument(document);
|
||||
return new ModCreateFile(new FutureVirtualFile(directory().getOriginalFile(),
|
||||
vf.getName(), vf.getFileType()), psiFile.getText());
|
||||
});
|
||||
@@ -271,17 +280,6 @@ final class PsiUpdateImpl {
|
||||
return tracker(file).getCopy(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteFile(@NotNull PsiFile file) {
|
||||
PsiFile originalFile = file.getOriginalFile();
|
||||
VirtualFile virtualFile = originalFile.getViewProvider().getVirtualFile();
|
||||
if (virtualFile instanceof ChangedVirtualDirectory.AddedVirtualFile avf) {
|
||||
avf.delete(null);
|
||||
} else {
|
||||
myDeletedFiles.add(virtualFile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
@@ -447,7 +445,6 @@ final class PsiUpdateImpl {
|
||||
.andThen(myChangedDirectories.values().stream()
|
||||
.flatMap(info -> info.createFileCommands(myTracker.myProject))
|
||||
.reduce(nop(), ModCommand::andThen))
|
||||
.andThen(myDeletedFiles.stream().<ModCommand>map(df -> new ModDeleteFile(df)).reduce(nop(), ModCommand::andThen))
|
||||
.andThen(getNavigateCommand()).andThen(getHighlightCommand()).andThen(myRenameSymbol == null ? nop() : myRenameSymbol);
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -33,6 +33,7 @@ public class PackageDotHtmlMayBePackageInfoInspection extends BaseInspection {
|
||||
return new PackageDotHtmlMayBePackageInfoFix(aPackage);
|
||||
}
|
||||
|
||||
@SuppressWarnings("DialogTitleCapitalization")
|
||||
@NotNull
|
||||
@Override
|
||||
protected String buildErrorString(Object... infos) {
|
||||
@@ -58,7 +59,7 @@ public class PackageDotHtmlMayBePackageInfoInspection extends BaseInspection {
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
if (element instanceof XmlFile xmlFile) {
|
||||
updater.deleteFile(xmlFile);
|
||||
xmlFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +105,7 @@ public class PackageDotHtmlMayBePackageInfoInspection extends BaseInspection {
|
||||
packageInfoFile.add(comment);
|
||||
}
|
||||
CodeStyleManager.getInstance(project).reformat(packageInfoFile);
|
||||
updater.deleteFile(xmlFile);
|
||||
xmlFile.delete();
|
||||
updater.moveTo(packageInfoFile);
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<warning descr="'package.html' may be converted to 'package-info.java'"><!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
Hello from documentation!
|
||||
<p>
|
||||
This is a package.
|
||||
</p>
|
||||
</body>
|
||||
</html></warning>
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
<warning descr="'package.html' may be converted to 'package-info.java'"><html>
|
||||
this is a test file
|
||||
</html></warning>
|
||||
+23
-16
@@ -1,29 +1,36 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.javadoc;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.openapi.application.impl.NonBlockingReadActionImpl;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.siyeh.ig.LightJavaInspectionTestCase;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PackageDotHtmlMayBePackageInfoInspectionTest extends LightJavaInspectionTestCase {
|
||||
|
||||
public void testSimple() {
|
||||
myFixture.configureByFile("package.html");
|
||||
myFixture.configureByFile("com/example/package.html");
|
||||
myFixture.testHighlighting(true, false, false);
|
||||
IntentionAction action = myFixture.findSingleIntention("Convert to 'package-info.java'");
|
||||
PsiFile psiFile = myFixture.getFile();
|
||||
PsiDirectory directory = psiFile.getContainingDirectory();
|
||||
assertTrue(psiFile.isValid());
|
||||
myFixture.launchAction(action);
|
||||
NonBlockingReadActionImpl.waitForAsyncTaskCompletion();
|
||||
assertFalse(psiFile.isValid());
|
||||
PsiFile packageInfo = directory.findFile("package-info.java");
|
||||
assertNotNull(packageInfo);
|
||||
assertEquals("""
|
||||
/**
|
||||
* Hello from documentation!
|
||||
* <p>
|
||||
* This is a package.
|
||||
* </p>
|
||||
*/
|
||||
package com.example;""", packageInfo.getText());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
Reference in New Issue
Block a user