[java] Improve support for multi-shred injections with guarded blocks in ModCommands

IDEA-333006 Language injection in String templates

GitOrigin-RevId: 6b6bec62bd709cd0419139d8a78925ee3d59e249
This commit is contained in:
Tagir Valeev
2024-01-24 08:47:44 +01:00
committed by intellij-monorepo-bot
parent 108f842a27
commit 1813854fea
6 changed files with 57 additions and 11 deletions

View File

@@ -0,0 +1,16 @@
// "Add on-demand static import for 'java.lang.System'" "true-preview"
import org.intellij.lang.annotations.Language;
public class Hello {
void test(String name, String message) {
@Language("JAVA")
String s = STR."""
import static java.lang.System.*;
class \{name} {
void main() {
out.println("\{message}");
}
}""";
}
}

View File

@@ -0,0 +1,14 @@
// "Add on-demand static import for 'java.lang.System'" "true-preview"
import org.intellij.lang.annotations.Language;
public class Hello {
void test(String name, String message) {
@Language("JAVA")
String s = STR."""
class \{name} {
void main() {
<caret>System.out.println("\{message}");
}
}""";
}
}

View File

@@ -16,8 +16,17 @@
package com.intellij.java.codeInsight.intention;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
import com.intellij.testFramework.LightProjectDescriptor;
import org.jetbrains.annotations.NotNull;
import static com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase.JAVA_21;
public class AddOnDemandStaticImportActionTest extends LightIntentionActionTestCase {
@Override
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
return JAVA_21;
}
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/addOnDemandStaticImport";

View File

@@ -65,7 +65,7 @@ final class PsiUpdateImpl {
}
private static class FileTracker implements DocumentListener, Disposable {
private final @Nullable PsiLanguageInjectionHost myHostCopy;
private final @Nullable SmartPsiElementPointer<PsiLanguageInjectionHost> myHostCopy;
private final @NotNull PsiFile myTargetFile;
private final @NotNull Document myPositionDocument;
private final @NotNull List<ModUpdateFileText.Fragment> myFragments = new ArrayList<>();
@@ -91,7 +91,8 @@ final class PsiUpdateImpl {
FileTracker hostTracker = changedFiles.get(hostFile);
PsiFile hostFileCopy = hostTracker != null ? hostTracker.myTargetFile : (PsiFile)hostFile.copy();
myInjectedFileCopy = getInjectedFileCopy(host, hostFileCopy, origFile.getLanguage());
myHostCopy = injectionManager.getInjectionHost(myInjectedFileCopy);
PsiLanguageInjectionHost injectionHost = injectionManager.getInjectionHost(myInjectedFileCopy);
myHostCopy = injectionHost == null ? null : SmartPointerManager.createPointer(injectionHost);
Disposable disposable = ApplicationManager.getApplication().getService(InjectionEditService.class)
.synchronizeWithFragment(myInjectedFileCopy, myDocument);
Disposer.register(this, disposable);
@@ -112,6 +113,10 @@ final class PsiUpdateImpl {
PostprocessReformattingAspect.getInstance(myProject).forcePostprocessFormat(myCopyFile, this);
}
@Nullable PsiLanguageInjectionHost getHostCopy() {
return myHostCopy == null ? null : myHostCopy.getElement();
}
void unblock() {
myManager.doPostponedOperationsAndUnblockDocument(myDocument);
}
@@ -417,7 +422,7 @@ final class PsiUpdateImpl {
@Override
public void moveCaretTo(int offset) {
myPositionUpdated = true;
PsiLanguageInjectionHost host = myTracker.myHostCopy;
PsiLanguageInjectionHost host = myTracker.getHostCopy();
if (host != null) {
InjectedLanguageManager instance = InjectedLanguageManager.getInstance(myTracker.myProject);
PsiFile file = findInjectedFile(instance, host);
@@ -505,7 +510,7 @@ final class PsiUpdateImpl {
}
private TextRange mapRange(@NotNull TextRange range) {
PsiLanguageInjectionHost host = myTracker.myHostCopy;
PsiLanguageInjectionHost host = myTracker.getHostCopy();
if (host != null) {
InjectedLanguageManager instance = InjectedLanguageManager.getInstance(myTracker.myProject);
PsiFile file = findInjectedFile(instance, host);

View File

@@ -25,7 +25,8 @@ public final class InjectionEditServiceImpl implements InjectionEditService {
Place shreds = InjectedLanguageUtilBase.getShreds(injectedFile);
Project project = injectedFile.getProject();
PsiLanguageInjectionHost host = Objects.requireNonNull(InjectedLanguageManager.getInstance(project).getInjectionHost(injectedFile));
Editor editor = new ImaginaryEditor(project, host.getContainingFile().getViewProvider().getDocument());
Document origDocument = host.getContainingFile().getFileDocument();
Editor editor = new ImaginaryEditor(project, origDocument);
InjectedFileChangesHandler handler = QuickEditHandler.getHandler(injectedFile, editor, shreds, copyDocument);
copyDocument.addDocumentListener(new DocumentListener() {
@Override
@@ -33,6 +34,7 @@ public final class InjectionEditServiceImpl implements InjectionEditService {
handler.commitToOriginal(event);
}
});
QuickEditHandler.initGuardedBlocks(copyDocument, origDocument, shreds);
return handler;
}
}

View File

@@ -165,7 +165,7 @@ public final class QuickEditHandler extends UserDataHolderBase implements Dispos
Disposer.register(this, () -> finalEditHandlers.remove(this));
});
initGuardedBlocks(shreds);
initGuardedBlocks(myNewDocument, myOrigDocument, shreds);
myOrigDocument.addDocumentListener(this, this);
myNewDocument.addDocumentListener(this, this);
@@ -312,7 +312,7 @@ public final class QuickEditHandler extends UserDataHolderBase implements Dispos
closeEditor();
}
private void initGuardedBlocks(Place shreds) {
static void initGuardedBlocks(@NotNull Document newDocument, @NotNull Document origDocument, Place shreds) {
int origOffset = -1;
int curOffset = 0;
for (PsiLanguageInjectionHost.Shred shred : shreds) {
@@ -320,16 +320,16 @@ public final class QuickEditHandler extends UserDataHolderBase implements Dispos
int start = shred.getRange().getStartOffset() + shred.getPrefix().length();
int end = shred.getRange().getEndOffset() - shred.getSuffix().length();
if (curOffset < start) {
RangeMarker guard = myNewDocument.createGuardedBlock(curOffset, start);
RangeMarker guard = newDocument.createGuardedBlock(curOffset, start);
if (curOffset == 0 && shred == shreds.get(0)) guard.setGreedyToLeft(true);
String padding = origOffset < 0 ? "" : myOrigDocument.getText().substring(origOffset, hostRangeMarker.getStartOffset());
String padding = origOffset < 0 ? "" : origDocument.getText().substring(origOffset, hostRangeMarker.getStartOffset());
guard.putUserData(REPLACEMENT_KEY, fixQuotes(padding));
}
curOffset = end;
origOffset = hostRangeMarker.getEndOffset();
}
if (curOffset < myNewDocument.getTextLength()) {
RangeMarker guard = myNewDocument.createGuardedBlock(curOffset, myNewDocument.getTextLength());
if (curOffset < newDocument.getTextLength()) {
RangeMarker guard = newDocument.createGuardedBlock(curOffset, newDocument.getTextLength());
guard.setGreedyToRight(true);
guard.putUserData(REPLACEMENT_KEY, "");
}