an initial version of IDEABKL-5496 Auto-filling the actual Java call arguments
authorpeter <peter@jetbrains.com>
Mon, 29 Aug 2016 12:29:26 +0000 (14:29 +0200)
committerpeter <peter@jetbrains.com>
Mon, 29 Aug 2016 12:33:30 +0000 (14:33 +0200)
java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallElement.java
platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java
platform/util/resources/misc/registry.properties

index 30e69895c245d18d4dc02d91cc2140e3a5cab004..9a6925cf58fd2275e8f30568be896ecb78feec79 100644 (file)
@@ -18,9 +18,16 @@ package com.intellij.codeInsight.completion;
 import com.intellij.codeInsight.completion.util.MethodParenthesesHandler;
 import com.intellij.codeInsight.lookup.*;
 import com.intellij.codeInsight.lookup.impl.JavaElementLookupRenderer;
+import com.intellij.codeInsight.template.Template;
+import com.intellij.codeInsight.template.TemplateManager;
+import com.intellij.codeInsight.template.impl.ConstantNode;
+import com.intellij.codeInsight.template.impl.MacroCallNode;
+import com.intellij.codeInsight.template.macro.CompleteMacro;
 import com.intellij.featureStatistics.FeatureUsageTracker;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.util.ClassConditionKey;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.pom.java.LanguageLevel;
 import com.intellij.psi.*;
 import com.intellij.psi.util.PsiTreeUtil;
@@ -148,15 +155,7 @@ public class JavaMethodCallElement extends LookupItem<PsiMethod> implements Type
     }
     else if (myHelper != null) {
       context.commitDocument();
-      if (willBeImported()) {
-        final PsiReferenceExpression ref = PsiTreeUtil.findElementOfClassAtOffset(file, startOffset, PsiReferenceExpression.class, false);
-        if (ref != null && myContainingClass != null && !ref.isReferenceTo(method)) {
-          ref.bindToElementViaStaticImport(myContainingClass);
-        }
-        return;
-      }
-
-      qualifyMethodCall(file, startOffset, document);
+      importOrQualify(document, file, method, startOffset);
     }
 
     final PsiType type = method.getReturnType();
@@ -171,6 +170,36 @@ public class JavaMethodCallElement extends LookupItem<PsiMethod> implements Type
       }
     }
 
+    if (hasParams && Registry.is("java.completion.argument.live.template")) {
+      startArgumentLiveTemplate(context, method);
+    }
+  }
+
+  private void importOrQualify(Document document, PsiFile file, PsiMethod method, int startOffset) {
+    if (willBeImported()) {
+      final PsiReferenceExpression ref = PsiTreeUtil.findElementOfClassAtOffset(file, startOffset, PsiReferenceExpression.class, false);
+      if (ref != null && myContainingClass != null && !ref.isReferenceTo(method)) {
+        ref.bindToElementViaStaticImport(myContainingClass);
+      }
+      return;
+    }
+
+    qualifyMethodCall(file, startOffset, document);
+  }
+
+  private static void startArgumentLiveTemplate(InsertionContext context, PsiMethod method) {
+    TemplateManager manager = TemplateManager.getInstance(method.getProject());
+    Template template = manager.createTemplate("", "");
+    PsiParameter[] parameters = method.getParameterList().getParameters();
+    for (int i = 0; i < parameters.length; i++) {
+      if (i > 0) {
+        template.addTextSegment(", ");
+      }
+      String name = StringUtil.notNullize(parameters[i].getName());
+      template.addVariable(name, new MacroCallNode(new CompleteMacro()), new ConstantNode(name), true);
+    }
+
+    manager.startTemplate(context.getEditor(), template);
   }
 
   private boolean shouldInsertTypeParameters() {
index c28cf3cdb9bc0c2f7443ce17edb465745556a920..2eb9ba6712aa3667d6f68a0d44af5d5b3737c9e8 100644 (file)
@@ -122,7 +122,7 @@ public class TemplateState implements Disposable {
     myLookupListener = new LookupAdapter() {
       @Override
       public void itemSelected(LookupEvent event) {
-        if (isCaretOutsideCurrentSegment()) {
+        if (isCaretOutsideCurrentSegment() && !isCaretInsideNextVariable()) {
           gotoEnd(true);
         }
       }
@@ -185,6 +185,15 @@ public class TemplateState implements Disposable {
     CommandProcessor.getInstance().addCommandListener(myCommandListener, this);
   }
 
+  private boolean isCaretInsideNextVariable() {
+    if (myEditor != null && myCurrentVariableNumber >= 0) {
+      int nextVar = getNextVariableNumber(myCurrentVariableNumber);
+      TextRange range = nextVar < 0 ? null : getVariableRange(myTemplate.getVariableNameAt(nextVar));
+      return range != null && range.containsOffset(myEditor.getCaretModel().getOffset());
+    }
+    return false;
+  }
+
   private boolean isCaretOutsideCurrentSegment() {
     if (myEditor != null && myCurrentSegmentNumber >= 0) {
       final int offset = myEditor.getCaretModel().getOffset();
@@ -535,13 +544,14 @@ public class TemplateState implements Disposable {
   }
 
   private int getCurrentSegmentNumber() {
-    if (myCurrentVariableNumber == -1) {
+    int varNumber = myCurrentVariableNumber;
+    if (varNumber == -1) {
       return -1;
     }
-    String variableName = myTemplate.getVariableNameAt(myCurrentVariableNumber);
+    String variableName = myTemplate.getVariableNameAt(varNumber);
     int segmentNumber = myTemplate.getVariableSegmentNumber(variableName);
     if (segmentNumber < 0) {
-      LOG.error("No segment for variable: var=" + myCurrentVariableNumber + "; name=" + variableName + "; " + presentTemplate(myTemplate) +
+      LOG.error("No segment for variable: var=" + varNumber + "; name=" + variableName + "; " + presentTemplate(myTemplate) +
                 "; offset: " + myEditor.getCaretModel().getOffset(), AttachmentFactory.createAttachment(myDocument));
     }
     return segmentNumber;
@@ -935,7 +945,7 @@ public class TemplateState implements Disposable {
     TextRange range = getCurrentVariableRange();
     if (range != null && range.getLength() > 0) {
       int caret = myEditor.getCaretModel().getOffset();
-      if (caret == range.getEndOffset()) {
+      if (caret == range.getEndOffset() || isCaretInsideNextVariable()) {
         nextTab();
       }
       else if (caret > range.getEndOffset()) {
index 7022841963e38fc696aedbe6d49f786669b4c699..2e439eb5dd7c816a7339b8338fb6e5fe5d34932e 100644 (file)
@@ -315,6 +315,9 @@ ide.completion.autopopup.choose.by.enter=true
 java.completion.make.outer.variables.final=true
 java.completion.make.outer.variables.final.description=Make variables accessed from inner class final automatically
 
+java.completion.argument.live.template=false
+java.completion.argument.live.template.description=When completing a method call, start a live template with all arguments
+
 java.annotations.inference.nullable.method=false
 java.annotations.inference.nullable.method.description=Restart is required; infer @Nullable annotation for method results
 java.annotations.inference.nullable.method.transitivity=true