diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaInjectedFileChangesHandlerProvider.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaInjectedFileChangesHandlerProvider.java index a1982bc82a03..c0e3f2a31b1c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaInjectedFileChangesHandlerProvider.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaInjectedFileChangesHandlerProvider.java @@ -1,4 +1,4 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 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.intellij.psi.impl.source.tree.injected; import com.intellij.codeInsight.editorActions.CopyPastePreProcessor; @@ -117,8 +117,10 @@ class OldJavaInjectedFileChangesHandler extends BaseInjectedFileChangesHandler { // reformat PsiDocumentManager.getInstance(myProject).commitDocument(myHostDocument); try { - CodeStyleManager.getInstance(myProject).reformatRange( - origPsiFile, hostStartOffset, myAltFullRange.getEndOffset(), true); + if (origPsiFile != null && origPsiFile.isPhysical()) { + CodeStyleManager.getInstance(myProject).reformatRange( + origPsiFile, hostStartOffset, myAltFullRange.getEndOffset(), true); + } } catch (IncorrectOperationException e1) { //LOG.error(e); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mergeFilter/afterComments.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mergeFilter/afterComments.java index 416e4deacaf3..1ad3b6ca88d3 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mergeFilter/afterComments.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mergeFilter/afterComments.java @@ -4,8 +4,8 @@ import java.util.stream.Stream; class Test { void foo(Stream stringStream ) { stringStream.filter(name -> name.startsWith("A") && name.//comment2 - length() > 1//comment - /*comment1*/ - ).findAny(); + length() > 1//comment + /*comment1*/ + ).findAny(); } } diff --git a/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.java b/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.java index ec221e97fc72..8bb1fa58c7a5 100644 --- a/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.java +++ b/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.java @@ -265,6 +265,31 @@ public final class InjectedLanguageManagerImpl extends InjectedLanguageManager i return visitor.unescapedOffset; } + @Override + public int mapUnescapedOffsetToInjected(@NotNull PsiFile injectedFile, int offset) { + if (offset < 0) return offset; + var visitor = new PsiRecursiveElementWalkingVisitor() { + int unescapedOffset = 0; + int escapedOffset = 0; + + @Override + public void visitElement(@NotNull PsiElement element) { + String leafText = InjectedLanguageUtilBase.getUnescapedLeafText(element, false); + if (leafText != null) { + unescapedOffset += leafText.length(); + escapedOffset += element.getTextLength(); + if (unescapedOffset >= offset) { + escapedOffset -= unescapedOffset - offset; + stopWalking(); + } + } + super.visitElement(element); + } + }; + injectedFile.accept(visitor); + return visitor.escapedOffset; + } + /** * intersection may spread over several injected fragments * @param rangeToEdit range in encoded(raw) PSI diff --git a/platform/core-api/src/com/intellij/lang/injection/InjectedLanguageManager.java b/platform/core-api/src/com/intellij/lang/injection/InjectedLanguageManager.java index 8987d12203c1..2bc4bd3be090 100644 --- a/platform/core-api/src/com/intellij/lang/injection/InjectedLanguageManager.java +++ b/platform/core-api/src/com/intellij/lang/injection/InjectedLanguageManager.java @@ -64,6 +64,11 @@ public abstract class InjectedLanguageManager { throw new UnsupportedOperationException(); } + @Contract(pure = true) + public int mapUnescapedOffsetToInjected(@NotNull PsiFile injectedFile, int offset) { + throw new UnsupportedOperationException(); + } + @NotNull public abstract List intersectWithAllEditableFragments(@NotNull PsiFile injectedPsi, @NotNull TextRange rangeToEdit); diff --git a/platform/lang-api/src/com/intellij/codeInspection/EditorUpdater.java b/platform/lang-api/src/com/intellij/codeInspection/EditorUpdater.java index c9149b20b3fe..b06d5c9c57d7 100644 --- a/platform/lang-api/src/com/intellij/codeInspection/EditorUpdater.java +++ b/platform/lang-api/src/com/intellij/codeInspection/EditorUpdater.java @@ -1,6 +1,7 @@ // 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.codeInspection; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -21,6 +22,13 @@ public interface EditorUpdater { */ void select(@NotNull PsiElement element); + /** + * Selects given range + * + * @param range range to select + */ + void select(@NotNull TextRange range); + /** * Navigates to a given element * diff --git a/platform/lang-api/src/com/intellij/codeInspection/ModCommands.java b/platform/lang-api/src/com/intellij/codeInspection/ModCommands.java index b9a64c2e1ff3..a0bed5c1672a 100644 --- a/platform/lang-api/src/com/intellij/codeInspection/ModCommands.java +++ b/platform/lang-api/src/com/intellij/codeInspection/ModCommands.java @@ -24,6 +24,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.LocalTimeCounter; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Objects; @@ -92,22 +93,25 @@ public final class ModCommands { PsiFile targetFile; Document positionDocument; boolean injected = injectionManager.isInjectedFragment(origFile); + PsiLanguageInjectionHost hostCopy; if (injected) { PsiLanguageInjectionHost host = Objects.requireNonNull(injectionManager.getInjectionHost(origFile)); PsiFile hostFile = host.getContainingFile(); PsiFile hostFileCopy = (PsiFile)hostFile.copy(); PsiFile injectedFileCopy = getInjectedFileCopy(host, hostFileCopy, orig.getLanguage()); + hostCopy = injectionManager.getInjectionHost(injectedFileCopy); disposable = ApplicationManager.getApplication().getService(InjectionEditService.class) .synchronizeWithFragment(injectedFileCopy, document); targetFile = hostFileCopy; origFile = hostFile; positionDocument = hostFileCopy.getViewProvider().getDocument(); } else { + hostCopy = null; disposable = null; targetFile = copyFile; positionDocument = document; } - EditorUpdaterImpl context = new EditorUpdaterImpl(actionContext, positionDocument, manager, document, injected, targetFile, copyFile); + EditorUpdaterImpl context = new EditorUpdaterImpl(actionContext, positionDocument, manager, document, hostCopy, copyFile); try { String oldText = targetFile.getText(); aspect.postponeFormattingInside( @@ -190,8 +194,7 @@ public final class ModCommands { private final @NotNull Document myPositionDocument; private final @NotNull PsiDocumentManager myManager; private final @NotNull Document myDocument; - private final boolean myInjected; - private final @NotNull PsiFile myTargetFile; + private final @Nullable PsiLanguageInjectionHost myHost; private final @NotNull PsiFile myCopyFile; private int myCaretOffset, mySelectionStart, mySelectionEnd; @@ -199,14 +202,12 @@ public final class ModCommands { @NotNull Document positionDocument, @NotNull PsiDocumentManager manager, @NotNull Document document, - boolean injected, - @NotNull PsiFile targetFile, + @Nullable PsiLanguageInjectionHost host, @NotNull PsiFile copyFile) { myPositionDocument = positionDocument; myManager = manager; myDocument = document; - myInjected = injected; - myTargetFile = targetFile; + myHost = host; myCopyFile = copyFile; myCaretOffset = actionContext.offset(); mySelectionStart = actionContext.selection().getStartOffset(); @@ -218,28 +219,51 @@ public final class ModCommands { public void select(@NotNull PsiElement element) { validate(element); myManager.doPostponedOperationsAndUnblockDocument(myDocument); - if (myInjected) { - element = PsiTreeUtil.findSameElementInCopy(element, myTargetFile); - } TextRange range = element.getTextRange(); + select(range); + } + + @Override + public void select(@NotNull TextRange range) { + if (myHost != null) { + InjectedLanguageManager instance = InjectedLanguageManager.getInstance(myCopyFile.getProject()); + PsiFile file = findInjectedFile(instance, myHost); + int start = instance.mapUnescapedOffsetToInjected(file, range.getStartOffset()); + int end = instance.mapUnescapedOffsetToInjected(file, range.getEndOffset()); + range = instance.injectedToHost(file, TextRange.create(start, end)); + } mySelectionStart = range.getStartOffset(); mySelectionEnd = range.getEndOffset(); myCaretOffset = range.getStartOffset(); } - + @Override public void moveTo(@NotNull PsiElement element) { validate(element); myManager.doPostponedOperationsAndUnblockDocument(myDocument); - if (myInjected) { - element = PsiTreeUtil.findSameElementInCopy(element, myTargetFile); - } int offset = element.getTextRange().getStartOffset(); - moveToOffset(offset); + if (myHost != null) { + InjectedLanguageManager instance = InjectedLanguageManager.getInstance(myCopyFile.getProject()); + PsiFile file = findInjectedFile(instance, myHost); + offset = instance.mapUnescapedOffsetToInjected(file, offset); + offset = instance.injectedToHost(file, offset); + } + myCaretOffset = offset; } - private void moveToOffset(int offset) { - myCaretOffset = offset; + private PsiFile findInjectedFile(InjectedLanguageManager instance, PsiLanguageInjectionHost host) { + var visitor = new PsiLanguageInjectionHost.InjectedPsiVisitor() { + PsiFile myFile = null; + + @Override + public void visit(@NotNull PsiFile injectedPsi, @NotNull List places) { + if (injectedPsi.getLanguage() == myCopyFile.getLanguage()) { + myFile = injectedPsi; + } + } + }; + instance.enumerate(host, visitor); + return Objects.requireNonNull(visitor.myFile); } @Override @@ -248,7 +272,7 @@ public final class ModCommands { String text = myPositionDocument.getText(); int idx = text.lastIndexOf(ch, myCaretOffset); if (idx == -1) return; - moveToOffset(idx); + myCaretOffset = idx; } private void validate(@NotNull PsiElement element) {