[javadoc] IDEA-285556 Support language injection into snippet

Fix injection for empty snippets. The range to inject is now starts after the colon and ends when the body ends. If no colon exist, then use the whole body as an injection point.

GitOrigin-RevId: c7961f13c30212f52aad45a121bcf11b70ce8f2b
This commit is contained in:
Nikita Eshkeev
2022-01-19 03:16:01 +03:00
committed by intellij-monorepo-bot
parent 06c3a7e94f
commit 715e914e30
7 changed files with 66 additions and 22 deletions

View File

@@ -1,18 +1,17 @@
// Copyright 2000-2021 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.javadoc;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.AbstractElementManipulator;
import com.intellij.psi.JavaDocTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.javadoc.PsiSnippetDocTagBody;
import com.intellij.psi.javadoc.PsiSnippetDocTagValue;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Optional;
public final class SnippetDocTagManipulator extends AbstractElementManipulator<PsiSnippetDocTagImpl> {
@Override
@@ -30,21 +29,34 @@ public final class SnippetDocTagManipulator extends AbstractElementManipulator<P
final PsiSnippetDocTagBody body = valueElement.getBody();
if (body == null) return super.getRangeInElement(element);
Optional<PsiElement> first = Arrays.stream(body.getChildren())
.filter(e -> e.getNode().getElementType() == JavaDocTokenType.DOC_COMMENT_DATA)
.findFirst();
if (!first.isPresent()) {
return super.getRangeInElement(element);
}
final PsiElement start = first.get();
PsiElement last = start;
for (PsiElement e = start.getNextSibling(); e != null; e = e.getNextSibling()) {
if (e.getNode().getElementType() == JavaDocTokenType.DOC_COMMENT_DATA) {
last = e;
}
}
final TextRange elementTextRange = element.getTextRange();
return TextRange.from(start.getTextRange().getStartOffset() - elementTextRange.getStartOffset(),
last.getTextRange().getEndOffset() - start.getTextRange().getStartOffset());
final PsiElement[] children = body.getChildren();
final int startOffset;
if (children.length == 0) {
startOffset = body.getTextRange().getStartOffset();
}
else {
final PsiElement colon = getColonElement(children);
startOffset = colon.getTextRange().getEndOffset();
}
final TextRange snippetRange = TextRange.create(startOffset, body.getTextRange().getEndOffset());
return snippetRange.shiftLeft(elementTextRange.getStartOffset());
}
private static @NotNull PsiElement getColonElement(PsiElement@NotNull [] children) {
final ASTNode node = children[0].getNode();
if (node == JavaDocTokenType.DOC_TAG_VALUE_COLON) {
return children[0];
}
final ASTNode colonNode = TreeUtil.findSibling(node, JavaDocTokenType.DOC_TAG_VALUE_COLON);
if (colonNode == null) {
return children[0];
}
return colonNode.getPsi();
}
}

View File

@@ -0,0 +1,6 @@
/**
* A simple program.
* {<warning descr="'@snippet' tag is not available at this language level">@snippet</warning> :}
*/
class A {
}

View File

@@ -0,0 +1,8 @@
/**
* A simple program.
* {<warning descr="'@snippet' tag is not available at this language level">@snippet</warning> :
*
* }
*/
class A {
}

View File

@@ -0,0 +1,6 @@
// "JAVA" "true"
/**
* {@snippet :<caret>}
*/
class InjectJava {}

View File

@@ -0,0 +1,9 @@
// "JAVA" "true"
/**
* A simple program.
* {@snippet :
* <caret>
* }
*/
class InjectJava {}

View File

@@ -129,6 +129,9 @@ public class JavadocHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testSnippet() { doTest(); }
public void testSnippetMethod() { doTest(); }
public void testSnippetInstructions() { doTest(); }
public void testEmptySnippet() { doTest(); }
public void testOnlyEmptyLinesInSnippet() { doTest(); }
public void testIssueLinksInJavaDoc() {
IssueNavigationConfiguration navigationConfiguration = IssueNavigationConfiguration.getInstance(getProject());

View File

@@ -8,6 +8,7 @@ import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.tree.injected.MyTestInjector;
import com.intellij.psi.javadoc.PsiSnippetDocTag;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.testFramework.LightProjectDescriptor;
import org.jetbrains.annotations.NotNull;
@@ -22,8 +23,6 @@ public class JavadocSnippetInjectionTest extends LightQuickFixParameterizedTestC
@Override
protected void setUp() throws Exception {
super.setUp();
MyTestInjector testInjector = new MyTestInjector(getPsiManager());
testInjector.injectAll(getTestRootDisposable());
}
@Override
@@ -39,8 +38,9 @@ public class JavadocSnippetInjectionTest extends LightQuickFixParameterizedTestC
@NotNull
private Language getInjectedLanguage() {
int offset = getEditor().getCaretModel().getPrimaryCaret().getOffset();
final PsiSnippetDocTag snippet = (PsiSnippetDocTag) PsiUtilCore.getElementAtOffset(getFile(), offset).getParent();
final int offset = getEditor().getCaretModel().getPrimaryCaret().getOffset();
final PsiElement element = PsiUtilCore.getElementAtOffset(getFile(), offset);
final PsiSnippetDocTag snippet = PsiTreeUtil.getParentOfType(element, PsiSnippetDocTag.class);
final AtomicReference<PsiElement> injected = new AtomicReference<>();
final InjectedLanguageManager injectionManager = InjectedLanguageManager.getInstance(getProject());
injectionManager.enumerate(snippet, (injectedPsi, places) -> { injected.set(injectedPsi); });