[java-completion] itemWithOverrideImplementDialog(): do not rely on the position at the LookupElement creation

When there are multiple carets, the LookupElement is deduplicated and the same one is applied for every caret position, so the same captured offset was used for every caret
Fixes EA-233292 - IAE: DocumentImpl.deleteString

GitOrigin-RevId: 61cf985408fc9cce72c2a8112ebd862c30e29e2e
This commit is contained in:
Tagir Valeev
2020-10-09 13:38:27 +07:00
committed by intellij-monorepo-bot
parent 849ca4541b
commit 03ccfba495
4 changed files with 63 additions and 8 deletions

View File

@@ -66,7 +66,7 @@ public final class JavaGenerateMemberCompletionContributor {
PsiAnnotation annotation = Objects.requireNonNull(PsiTreeUtil.getParentOfType(position, PsiAnnotation.class));
int annoStart = annotation.getTextRange().getStartOffset();
result.addElement(itemWithOverrideImplementDialog(annoStart));
result.addElement(itemWithOverrideImplementDialog());
suggestGeneratedMethods(
result.withPrefixMatcher(new NoMiddleMatchesAfterSpace(annotation.getText().substring(0, parameters.getOffset() - annoStart))),
@@ -99,15 +99,18 @@ public final class JavaGenerateMemberCompletionContributor {
}
@NotNull
private static LookupElementBuilder itemWithOverrideImplementDialog(int annoStart) {
private static LookupElementBuilder itemWithOverrideImplementDialog() {
return LookupElementBuilder.create(JavaBundle.message("completion.override.implement.methods")).withLookupString("Override")
.withInsertHandler((context, item) -> {
context.getDocument().deleteString(annoStart, context.getTailOffset());
context.commitDocument();
context.setAddCompletionChar(false);
context.setLaterRunnable(() -> {
new OverrideMethodsHandler().invoke(context.getProject(), context.getEditor(), context.getFile());
});
PsiAnnotation annotation = PsiTreeUtil.getParentOfType(context.getFile().findElementAt(context.getStartOffset()), PsiAnnotation.class);
if (annotation != null) {
context.getDocument().deleteString(annotation.getTextRange().getStartOffset(), context.getTailOffset());
context.commitDocument();
}
context.setAddCompletionChar(false);
context.setLaterRunnable(() -> {
new OverrideMethodsHandler().invoke(context.getProject(), context.getEditor(), context.getFile());
});
});
}

View File

@@ -0,0 +1,12 @@
class Test {
@Over<caret>
@Over<caret>
@Over<caret>
@Over<caret>
@Over<caret>
@Over<caret>
@Over<caret>
@Over<caret>
@Over<caret>
@Over<caret>
}

View File

@@ -0,0 +1,32 @@
class Test {
public Test() {
super();
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return super.toString();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
}
}

View File

@@ -1633,6 +1633,14 @@ class XInternalError {}
checkResult()
}
@NeedsIndex.SmartMode(reason = "JavaGenerateMemberCompletionContributor.fillCompletionVariants provides dialog option in smart mode only")
void testSuggestToOverrideMethodsInMulticaretMode() {
configure()
myFixture.assertPreferredCompletionItems 0, 'Override/Implement methods...', 'Override'
myFixture.type('\n')
checkResult()
}
@NeedsIndex.SmartMode(reason = "JavaGenerateMemberCompletionContributor.fillCompletionVariants works in smart mode only (for implementing methods)")
void testStrikeOutDeprecatedSuperMethods() {
configure()