mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-CR-52982: GO-8123 fix getOriginalElement during postfix template completion
GitOrigin-RevId: 73f3045e4c07b5ca65e512877d55f3d2dde436af
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b84c228f47
commit
10bf03cc53
+7
@@ -0,0 +1,7 @@
|
||||
public class Foo {
|
||||
void m(boolean x) {
|
||||
x.foo<caret>
|
||||
}
|
||||
void m2() {
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
public class Foo {
|
||||
void m(boolean x) {
|
||||
System.out.println();<caret>
|
||||
}
|
||||
void m2() {
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// Copyright 2000-2019 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.
|
||||
package com.intellij.java.codeInsight.template.postfix.templates;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionUtilCoreImpl;
|
||||
import com.intellij.codeInsight.template.postfix.settings.PostfixTemplateStorage;
|
||||
import com.intellij.codeInsight.template.postfix.templates.JavaPostfixTemplateProvider;
|
||||
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate;
|
||||
import com.intellij.codeInsight.template.postfix.templates.editable.JavaEditablePostfixTemplate;
|
||||
import com.intellij.codeInsight.template.postfix.templates.editable.JavaPostfixTemplateExpressionCondition;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class OriginalElementPostfixTemplateTest extends PostfixTemplateTestCase {
|
||||
private static final JavaPostfixTemplateProvider PROVIDER = new JavaPostfixTemplateProvider();
|
||||
private Set<PostfixTemplate> myOriginalTemplates;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
myOriginalTemplates = PROVIDER.getTemplates();
|
||||
// Register a custom condition which returns true if it can get an original element of the next method.
|
||||
// This emulates conditions in some languages e.g. in go where resolve involves getOriginalElement() calls.
|
||||
PostfixTemplate template = new JavaEditablePostfixTemplate(
|
||||
"myId", "foo", "System.out.println();$END$", "",
|
||||
ContainerUtil.set(new OriginalElementCondition()),
|
||||
LanguageLevel.JDK_1_8, true, PROVIDER);
|
||||
PostfixTemplateStorage.getInstance().setTemplates(PROVIDER, asList(template));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
try {
|
||||
PostfixTemplateStorage.getInstance().setTemplates(PROVIDER, myOriginalTemplates);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
addSuppressedException(e);
|
||||
}
|
||||
finally {
|
||||
super.tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testOriginalElement() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getSuffix() {
|
||||
return "originalElement";
|
||||
}
|
||||
|
||||
private static class OriginalElementCondition implements JavaPostfixTemplateExpressionCondition {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPresentableName() {
|
||||
return "customCondition";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getId() {
|
||||
return "customCondition";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean value(@NotNull PsiExpression expression) {
|
||||
PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class);
|
||||
PsiMethod nextMethod = PsiTreeUtil.getNextSiblingOfType(method, PsiMethod.class);
|
||||
return nextMethod != null && CompletionUtilCoreImpl.getOriginalElement(nextMethod) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeTo(@NotNull Element element) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,14 +18,14 @@ import java.util.LinkedList;
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
class OffsetTranslator implements Disposable {
|
||||
public class OffsetTranslator implements Disposable {
|
||||
static final Key<OffsetTranslator> RANGE_TRANSLATION = Key.create("completion.rangeTranslation");
|
||||
|
||||
private final PsiFile myOriginalFile;
|
||||
private final Document myCopyDocument;
|
||||
private final LinkedList<DocumentEvent> myTranslation = new LinkedList<>();
|
||||
|
||||
OffsetTranslator(Document originalDocument, PsiFile originalFile, Document copyDocument, int start, int end, String replacement) {
|
||||
public OffsetTranslator(Document originalDocument, PsiFile originalFile, Document copyDocument, int start, int end, String replacement) {
|
||||
myOriginalFile = originalFile;
|
||||
myCopyDocument = copyDocument;
|
||||
myCopyDocument.putUserData(RANGE_TRANSLATION, this);
|
||||
|
||||
+36
-11
@@ -2,6 +2,7 @@
|
||||
package com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.codeInsight.completion.OffsetTranslator;
|
||||
import com.intellij.codeInsight.template.CustomLiveTemplateBase;
|
||||
import com.intellij.codeInsight.template.CustomTemplateCallback;
|
||||
import com.intellij.codeInsight.template.impl.CustomLiveTemplateLookupElement;
|
||||
@@ -11,6 +12,7 @@ import com.intellij.diagnostic.AttachmentFactory;
|
||||
import com.intellij.featureStatistics.FeatureUsageTracker;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.LanguageUtil;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.undo.UndoConstants;
|
||||
@@ -20,6 +22,7 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Conditions;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
@@ -205,19 +208,26 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
int offset) {
|
||||
Collection<CustomLiveTemplateLookupElement> result = new HashSet<>();
|
||||
CustomTemplateCallback callback = new CustomTemplateCallback(editor, file);
|
||||
for (PostfixTemplateProvider provider : LanguagePostfixTemplate.LANG_EP.allForLanguage(getLanguage(callback))) {
|
||||
ProgressManager.checkCanceled();
|
||||
String key = computeTemplateKeyWithoutContextChecking(callback);
|
||||
if (key != null && editor.getCaretModel().getCaretCount() == 1) {
|
||||
Condition<PostfixTemplate> isApplicationTemplateFunction = createIsApplicationTemplateFunction(provider, key, file, editor);
|
||||
for (PostfixTemplate postfixTemplate : PostfixTemplatesUtils.getAvailableTemplates(provider)) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (isApplicationTemplateFunction.value(postfixTemplate)) {
|
||||
result.add(new PostfixTemplateLookupElement(this, postfixTemplate, postfixTemplate.getKey(), provider, false));
|
||||
Disposable parentDisposable = Disposer.newDisposable();
|
||||
try {
|
||||
for (PostfixTemplateProvider provider : LanguagePostfixTemplate.LANG_EP.allForLanguage(getLanguage(callback))) {
|
||||
ProgressManager.checkCanceled();
|
||||
String key = computeTemplateKeyWithoutContextChecking(callback);
|
||||
if (key != null && editor.getCaretModel().getCaretCount() == 1) {
|
||||
Condition<PostfixTemplate> isApplicationTemplateFunction =
|
||||
createIsApplicationTemplateFunction(provider, key, file, editor, parentDisposable);
|
||||
for (PostfixTemplate postfixTemplate : PostfixTemplatesUtils.getAvailableTemplates(provider)) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (isApplicationTemplateFunction.value(postfixTemplate)) {
|
||||
result.add(new PostfixTemplateLookupElement(this, postfixTemplate, postfixTemplate.getKey(), provider, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(parentDisposable);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -256,7 +266,8 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
private static Condition<PostfixTemplate> createIsApplicationTemplateFunction(@NotNull final PostfixTemplateProvider provider,
|
||||
@NotNull String key,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull Editor editor) {
|
||||
@NotNull Editor editor,
|
||||
@NotNull Disposable parentDisposable) {
|
||||
if (file.getFileType().isBinary()) {
|
||||
return Conditions.alwaysFalse();
|
||||
}
|
||||
@@ -279,6 +290,14 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
return Conditions.alwaysFalse();
|
||||
}
|
||||
|
||||
Document originalDocument = file.getViewProvider().getDocument();
|
||||
if (originalDocument != null) {
|
||||
// The copy document doesn't contain live template key.
|
||||
// Register offset translator to make getOriginalElement() work in the copy.
|
||||
OffsetTranslator translator = new OffsetTranslator(originalDocument, file, copyDocument, newOffset, currentOffset, "");
|
||||
Disposer.register(parentDisposable, translator);
|
||||
}
|
||||
|
||||
final PsiElement context = CustomTemplateCallback.getContext(copyFile, positiveOffset(newOffset));
|
||||
final Document finalCopyDocument = copyDocument;
|
||||
return template -> template != null && template.isEnabled(provider) && template.isApplicable(context, finalCopyDocument, newOffset);
|
||||
@@ -314,7 +333,13 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
@NotNull PsiFile file,
|
||||
@NotNull Editor editor,
|
||||
@Nullable PostfixTemplate template) {
|
||||
return createIsApplicationTemplateFunction(provider, key, file, editor).value(template);
|
||||
Disposable parentDisposable = Disposer.newDisposable();
|
||||
try {
|
||||
return createIsApplicationTemplateFunction(provider, key, file, editor, parentDisposable).value(template);
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(parentDisposable);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
Reference in New Issue
Block a user