IDEA-58556 Make smart completion cast insertion aware of autoboxing

This commit is contained in:
peter
2011-02-23 11:33:13 +01:00
parent aa65664d63
commit c13f0636c2
8 changed files with 135 additions and 43 deletions
@@ -8,7 +8,6 @@ import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.util.Key;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.psi.*;
@@ -16,9 +15,11 @@ import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.impl.source.tree.java.PsiEmptyExpressionImpl;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author peter
@@ -27,7 +28,6 @@ class SmartCastProvider extends CompletionProvider<CompletionParameters> {
static final PsiElementPattern.Capture<PsiElement> INSIDE_TYPECAST_TYPE = PlatformPatterns.psiElement().afterLeaf(
PlatformPatterns.psiElement().withText("(").withParent(
PsiTypeCastExpression.class));
public static final Key<Boolean> TYPE_CAST = Key.create("TYPE_CAST");
protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
for (final ExpectedTypeInfo type : JavaSmartCompletionContributor.getExpectedTypes(parameters)) {
@@ -35,50 +35,90 @@ class SmartCastProvider extends CompletionProvider<CompletionParameters> {
continue;
}
final boolean overwrite = INSIDE_TYPECAST_TYPE.accepts(parameters.getOriginalPosition());
final PsiElement originalPosition = parameters.getOriginalPosition();
final boolean overwrite = INSIDE_TYPECAST_TYPE.accepts(originalPosition);
final LookupElement item = AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE.applyPolicy(new LookupElementDecorator<LookupItem>(
PsiTypeLookupItem.createLookupItem(type.getDefaultType(), parameters.getPosition())) {
@Override
public void handleInsert(InsertionContext context) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.smarttype.casting");
final Editor editor = context.getEditor();
final Document document = editor.getDocument();
if (overwrite) {
document.deleteString(context.getSelectionEndOffset(),
context.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET));
final PsiType defaultType = type.getDefaultType();
result.addElement(createSmartCastElement(parameters, overwrite, defaultType));
if (defaultType instanceof PsiPrimitiveType) {
final PsiType type1 = getCastedExpressionType(originalPosition);
if (type1 != null && !(type1 instanceof PsiPrimitiveType)) {
final PsiClassType boxedType = ((PsiPrimitiveType)defaultType).getBoxedType(originalPosition);
if (boxedType != null) {
result.addElement(createSmartCastElement(parameters, overwrite, boxedType));
}
final CodeStyleSettings csSettings = CodeStyleSettingsManager.getSettings(context.getProject());
final int oldTail = context.getTailOffset();
context.setTailOffset(RParenthTailType.addRParenth(editor, oldTail, csSettings.SPACE_WITHIN_CAST_PARENTHESES));
//todo let the delegate handle insertion itself
final LookupItem typeItem = getDelegate();
final InsertionContext typeContext = CompletionUtil.newContext(context, typeItem, context.getStartOffset(), oldTail);
new DefaultInsertHandler().handleInsert(typeContext, typeItem);
final PsiTypeCastExpression castExpression =
PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiTypeCastExpression.class, false);
if (castExpression != null) {
final PsiTypeElement typeElement = castExpression.getCastType();
if (typeElement != null) {
CodeStyleManager.getInstance(context.getProject()).reformat(typeElement);
}
}
PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting();
if (csSettings.SPACE_AFTER_TYPE_CAST) {
context.setTailOffset(TailType.insertChar(editor, context.getTailOffset(), ' '));
}
editor.getCaretModel().moveToOffset(context.getTailOffset());
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
}
});
item.putUserData(TYPE_CAST, Boolean.TRUE);
result.addElement(item);
}
}
}
@Nullable
private static PsiType getCastedExpressionType(PsiElement originalPosition) {
if (INSIDE_TYPECAST_TYPE.accepts(originalPosition)) {
final PsiTypeCastExpression cast = PsiTreeUtil.getParentOfType(originalPosition, PsiTypeCastExpression.class);
if (cast != null) {
final PsiExpression operand = cast.getOperand();
return operand == null ? null : operand.getType();
}
}
final PsiParenthesizedExpression parens = PsiTreeUtil.getParentOfType(originalPosition, PsiParenthesizedExpression.class, true, PsiStatement.class);
if (parens != null) {
final PsiExpression rightSide = parens.getExpression();
if (rightSide != null) {
return rightSide.getType();
}
PsiElement next = parens.getNextSibling();
while (next != null && (next instanceof PsiEmptyExpressionImpl || next instanceof PsiErrorElement || next instanceof PsiWhiteSpace)) {
next = next.getNextSibling();
}
if (next instanceof PsiExpression) {
return ((PsiExpression)next).getType();
}
return null;
}
return null;
}
private static LookupElement createSmartCastElement(final CompletionParameters parameters, final boolean overwrite, final PsiType type) {
return AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE.applyPolicy(new LookupElementDecorator<LookupItem>(
PsiTypeLookupItem.createLookupItem(type, parameters.getPosition())) {
@Override
public void handleInsert(InsertionContext context) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.smarttype.casting");
final Editor editor = context.getEditor();
final Document document = editor.getDocument();
if (overwrite) {
document.deleteString(context.getSelectionEndOffset(),
context.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET));
}
final CodeStyleSettings csSettings = CodeStyleSettingsManager.getSettings(context.getProject());
final int oldTail = context.getTailOffset();
context.setTailOffset(RParenthTailType.addRParenth(editor, oldTail, csSettings.SPACE_WITHIN_CAST_PARENTHESES));
//todo let the delegate handle insertion itself
final LookupItem typeItem = getDelegate();
final InsertionContext typeContext = CompletionUtil.newContext(context, typeItem, context.getStartOffset(), oldTail);
new DefaultInsertHandler().handleInsert(typeContext, typeItem);
final PsiTypeCastExpression castExpression =
PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiTypeCastExpression.class, false);
if (castExpression != null) {
final PsiTypeElement typeElement = castExpression.getCastType();
if (typeElement != null) {
CodeStyleManager.getInstance(context.getProject()).reformat(typeElement);
}
}
PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting();
if (csSettings.SPACE_AFTER_TYPE_CAST) {
context.setTailOffset(TailType.insertChar(editor, context.getTailOffset(), ' '));
}
editor.getCaretModel().moveToOffset(context.getTailOffset());
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
}
});
}
}
@@ -0,0 +1,8 @@
class A {
public abstract void zoo(int y);
{
Object x;
zoo((Integer) <caret>x);
}
}
@@ -0,0 +1,8 @@
class A {
public abstract void zoo(int y);
{
Object x;
zoo((<caret>x);
}
}
@@ -0,0 +1,8 @@
class A {
public abstract void zoo(int y);
{
Object x;
zoo((Integer) <caret>x);
}
}
@@ -0,0 +1,8 @@
class A {
public abstract void zoo(int y);
{
Object x;
zoo((<caret>) x);
}
}
@@ -0,0 +1,8 @@
class A {
public abstract void zoo(int y);
{
Object x;
zoo((Integer) <caret>x);
}
}
@@ -0,0 +1,8 @@
class A {
public abstract void zoo(int y);
{
Object x;
zoo((<caret>a) x);
}
}
@@ -431,6 +431,10 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase {
public void testPrivateOverloads() throws Throwable { doTest(); }
public void testCastAutoboxing() throws Throwable { doActionItemTest(); }
public void testCastAutoboxing2() throws Throwable { doActionItemTest(); }
public void testCastAutoboxing3() throws Throwable { doActionItemTest(); }
public void testNoFieldsInSuperConstructorCall() throws Throwable { doTest(); }
public void testNoUninitializedFieldsInConstructor() throws Throwable { doTest(); }
public void testFieldsSetInAnotherConstructor() throws Throwable { doTest(); }