From 58a6d887a7b4603e453c8d2490ba0bec594c5994 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 13 Oct 2009 18:24:23 +0100 Subject: [PATCH] IDEADEV-40766 Regr: Smart type completion at cast position insert wrong type for arrays --- .../com/intellij/codeInsight/TailTypes.java | 18 ----- .../CastingLookupElementDecorator.java | 2 +- .../completion/JavaChainLookupElement.java | 2 +- .../completion/JavaCompletionUtil.java | 2 +- .../JavaSmartCompletionContributor.java | 44 +++-------- .../completion/SmartCastProvider.java | 76 +++++++++++++++++++ .../completion/SmartCompletionDecorator.java | 2 +- .../completion/simple/RParenthTailType.java | 20 ++--- .../completion/CompletionUtil.java | 30 ++++++-- 9 files changed, 122 insertions(+), 74 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java diff --git a/java/java-impl/src/com/intellij/codeInsight/TailTypes.java b/java/java-impl/src/com/intellij/codeInsight/TailTypes.java index 5fc422228e71..41b29c8cc644 100644 --- a/java/java-impl/src/com/intellij/codeInsight/TailTypes.java +++ b/java/java-impl/src/com/intellij/codeInsight/TailTypes.java @@ -18,28 +18,10 @@ package com.intellij.codeInsight; import com.intellij.codeInsight.completion.simple.BracesTailType; import com.intellij.codeInsight.completion.simple.ParenthesesTailType; import com.intellij.codeInsight.completion.simple.RParenthTailType; -import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.openapi.editor.Editor; import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; -import org.jetbrains.annotations.NonNls; public class TailTypes { - public static final TailType CAST_RPARENTH = new RParenthTailType(){ - protected boolean isSpaceWithinParentheses(final CodeStyleSettings styleSettings, final Editor editor, final int tailOffset) { - editor.getCaretModel().moveToOffset(tailOffset); - return styleSettings.SPACE_WITHIN_CAST_PARENTHESES; - } - - public int processTail(final Editor editor, int tailOffset) { - FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.smarttype.casting"); - tailOffset = super.processTail(editor, tailOffset); - if (CodeStyleSettingsManager.getSettings(editor.getProject()).SPACE_AFTER_TYPE_CAST){ - tailOffset = insertChar(editor, tailOffset, ' '); - } - return tailOffset; - } - }; public static final TailType CALL_RPARENTH = new RParenthTailType(){ protected boolean isSpaceWithinParentheses(final CodeStyleSettings styleSettings, final Editor editor, final int tailOffset) { return styleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES && editor.getDocument().getCharsSequence().charAt(tailOffset - 1) != '('; diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/CastingLookupElementDecorator.java b/java/java-impl/src/com/intellij/codeInsight/completion/CastingLookupElementDecorator.java index 81bcfa034109..85ddb7183e68 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/CastingLookupElementDecorator.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/CastingLookupElementDecorator.java @@ -65,7 +65,7 @@ public class CastingLookupElementDecorator extends LookupElementDecorator TYPE_CAST = Key.create("TYPE_CAST"); static final ElementPattern INSIDE_TYPECAST_EXPRESSION = psiElement().withParent( psiElement(PsiReferenceExpression.class).afterLeaf( psiElement().withText(")").withParent(PsiTypeCastExpression.class))); - private static final PsiElementPattern.Capture INSIDE_TYPECAST_TYPE = psiElement().afterLeaf(psiElement().withText("(").withParent( - PsiTypeCastExpression.class)); @Nullable private static ElementFilter getReferenceFilter(PsiElement element) { @@ -125,34 +126,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor { public JavaSmartCompletionContributor() { - extend(CompletionType.SMART, INSIDE_TYPECAST_TYPE, new CompletionProvider() { - protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) { - for (final ExpectedTypeInfo type : getExpectedTypes(parameters)) { - if (type.getType() == PsiType.VOID) { - continue; - } - - final boolean overwrite = INSIDE_TYPECAST_TYPE.accepts(parameters.getOriginalPosition()); - - final LookupElement item = AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE.applyPolicy(new LookupElementDecorator(PsiTypeLookupItem.createLookupItem(type.getDefaultType())) { - - @Override - public void handleInsert(InsertionContext context) { - final Editor editor = context.getEditor(); - if (overwrite) { - editor.getDocument().deleteString(context.getSelectionEndOffset(), - context.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET)); - } - - TailTypes.CAST_RPARENTH.processTail(editor, context.getTailOffset()); - DefaultInsertHandler.addImportForItem(context.getFile(), context.getStartOffset(), getDelegate()); - } - }); - item.putUserData(TYPE_CAST, Boolean.TRUE); - result.addElement(item); - } - } - }); + extend(CompletionType.SMART, SmartCastProvider.INSIDE_TYPECAST_TYPE, new SmartCastProvider()); extend(CompletionType.SMART, psiElement().beforeLeaf(psiElement(JavaTokenType.RPARENTH)).afterLeaf("(").withParent( diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java new file mode 100644 index 000000000000..67fb7cb70360 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java @@ -0,0 +1,76 @@ +package com.intellij.codeInsight.completion; + +import com.intellij.codeInsight.ExpectedTypeInfo; +import com.intellij.codeInsight.TailType; +import com.intellij.codeInsight.completion.simple.RParenthTailType; +import com.intellij.codeInsight.lookup.*; +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.PsiElement; +import com.intellij.psi.PsiType; +import com.intellij.psi.PsiTypeCastExpression; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.impl.source.PostprocessReformattingAspect; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; + +/** + * @author peter + */ +class SmartCastProvider extends CompletionProvider { + static final PsiElementPattern.Capture INSIDE_TYPECAST_TYPE = PlatformPatterns.psiElement().afterLeaf( + PlatformPatterns.psiElement().withText("(").withParent( + PsiTypeCastExpression.class)); + public static final Key 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)) { + if (type.getType() == PsiType.VOID) { + continue; + } + + final boolean overwrite = INSIDE_TYPECAST_TYPE.accepts(parameters.getOriginalPosition()); + + final LookupElement item = AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE.applyPolicy(new LookupElementDecorator( + PsiTypeLookupItem.createLookupItem(type.getDefaultType())) { + + @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); + 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); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/SmartCompletionDecorator.java b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCompletionDecorator.java index 33adb896553f..2aedb036da85 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/SmartCompletionDecorator.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCompletionDecorator.java @@ -219,7 +219,7 @@ public class SmartCompletionDecorator extends TailTypeDecorator { && item.getUserData(LookupItem.BRACKETS_COUNT_ATTR) == null && enclosing instanceof PsiNewExpression && !(position instanceof PsiParenthesizedExpression) - && item.getUserData(JavaSmartCompletionContributor.TYPE_CAST) == null + && item.getUserData(SmartCastProvider.TYPE_CAST) == null && PsiTreeUtil.getContextOfType(position, PsiReferenceParameterList.class, false) == null ) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/simple/RParenthTailType.java b/java/java-impl/src/com/intellij/codeInsight/completion/simple/RParenthTailType.java index f6f334ba4cb4..9520d1b3e61c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/simple/RParenthTailType.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/simple/RParenthTailType.java @@ -52,20 +52,22 @@ public abstract class RParenthTailType extends TailType { protected abstract boolean isSpaceWithinParentheses(CodeStyleSettings styleSettings, Editor editor, final int tailOffset); public int processTail(final Editor editor, int tailOffset) { - CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(editor.getProject()); - int existingRParenthOffset = getExistingRParenthOffset(editor, tailOffset); + return addRParenth(editor, tailOffset, isSpaceWithinParentheses(CodeStyleSettingsManager.getSettings(editor.getProject()), editor, tailOffset)); + } + + public static int addRParenth(Editor editor, int offset, boolean spaceWithinParens) { + int existingRParenthOffset = getExistingRParenthOffset(editor, offset); - boolean spaceWithinParens = isSpaceWithinParentheses(styleSettings, editor, tailOffset); if (existingRParenthOffset < 0){ if (spaceWithinParens){ - tailOffset = insertChar(editor, tailOffset, ' '); + offset = insertChar(editor, offset, ' '); } - editor.getDocument().insertString(tailOffset, ")"); - return moveCaret(editor, tailOffset, 1); + editor.getDocument().insertString(offset, ")"); + return moveCaret(editor, offset, 1); + } + if (spaceWithinParens && offset == existingRParenthOffset) { + existingRParenthOffset = insertChar(editor, offset, ' '); } - if (spaceWithinParens && tailOffset == existingRParenthOffset) { - existingRParenthOffset = insertChar(editor, tailOffset, ' '); - } return moveCaret(editor, existingRParenthOffset, 1); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java index 89a45e855b66..dd9b2ac3fc51 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java @@ -142,19 +142,25 @@ public class CompletionUtil { return text.substring(start + 1, offsetInElement).trim(); } - static InsertionContext emulateInsertion(InsertionContext oldContext, int newStart, final LookupElement item, char completionChar) { - final Editor editor = oldContext.getEditor(); - final Document document = editor.getDocument(); - final InsertionContext newContext = new InsertionContext(new OffsetMap(document), completionChar, LookupElement.EMPTY_ARRAY, oldContext.getFile(), editor); + static InsertionContext emulateInsertion(InsertionContext oldContext, int newStart, final LookupElement item) { + final InsertionContext newContext = newContext(oldContext, item); emulateInsertion(item, newStart, newContext); return newContext; } + private static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement) { + final Editor editor = oldContext.getEditor(); + return new InsertionContext(new OffsetMap(editor.getDocument()), Lookup.AUTO_INSERT_SELECT_CHAR, new LookupElement[]{forElement}, oldContext.getFile(), editor); + } + + public static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement, int startOffset, int tailOffset) { + final InsertionContext context = newContext(oldContext, forElement); + setOffsets(context, startOffset, tailOffset); + return context; + } + public static void emulateInsertion(LookupElement item, int offset, InsertionContext context) { - context.getOffsetMap().addOffset(CompletionInitializationContext.START_OFFSET, offset); - context.getOffsetMap().addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, offset); - context.getOffsetMap().addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, offset); - context.setTailOffset(offset); + setOffsets(context, offset, offset); final Editor editor = context.getEditor(); final Document document = editor.getDocument(); @@ -165,4 +171,12 @@ public class CompletionUtil { PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); item.handleInsert(context); } + + private static void setOffsets(InsertionContext context, int offset, final int tailOffset) { + final OffsetMap offsetMap = context.getOffsetMap(); + offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset); + offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, tailOffset); + offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, tailOffset); + context.setTailOffset(tailOffset); + } }