diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java
index 6ce487786b3..fa9c5aadc94 100644
--- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java
+++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java
@@ -272,4 +272,9 @@ public class JetPsiFactory {
public static JetExpressionCodeFragment createExpressionCodeFragment(Project project, String text, PsiElement context) {
return new JetExpressionCodeFragmentImpl(project, "fragment.kt", text, context);
}
+
+ public static JetExpression createFunctionBody(Project project, @NotNull String bodyText) {
+ JetFunction func = createFunction(project, "fun foo() {\n" + bodyText + "\n}");
+ return func.getBodyExpression();
+ }
}
diff --git a/idea/resources/fileTemplates/code/New Kotlin Method Body.kt.ft b/idea/resources/fileTemplates/code/New Kotlin Method Body.kt.ft
new file mode 100644
index 00000000000..623da6c89fa
--- /dev/null
+++ b/idea/resources/fileTemplates/code/New Kotlin Method Body.kt.ft
@@ -0,0 +1 @@
+//To change body of created methods use File | Settings | File Templates.
\ No newline at end of file
diff --git a/idea/resources/fileTemplates/code/New Kotlin Method Body.kt.html b/idea/resources/fileTemplates/code/New Kotlin Method Body.kt.html
new file mode 100644
index 00000000000..c3526f30920
--- /dev/null
+++ b/idea/resources/fileTemplates/code/New Kotlin Method Body.kt.html
@@ -0,0 +1,38 @@
+
+
+
+
+ This is a built-in template used for filling the body of a Kotlin method
+ each time it is generated by the program, e.g. when using the Create Method from Usage intention action.
+ The template is editable. Along with Kotlin expressions and comments, you can also use the predefined variables
+ that will be then expanded into the corresponding values.
+ |
+
+
+
+
+ | Predefined variables will take the following values: |
+
+
+ | ${RETURN_TYPE} |
+ |
+ a return type of a created method |
+
+
+ | ${METHOD_NAME} |
+ |
+ name of the created method |
+
+
+ | ${CLASS_NAME} |
+ |
+ qualified name of the class where method is created |
+
+
+ | ${SIMPLE_CLASS_NAME} |
+ |
+ non-qualified name of the class where method is implemented |
+
+
+
+
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java
index 0182e96d73a..55d7a444144 100644
--- a/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java
+++ b/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java
@@ -22,8 +22,11 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.template.*;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.codeInsight.template.impl.Variable;
+import com.intellij.ide.fileTemplates.FileTemplate;
+import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
@@ -33,12 +36,14 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
+import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeProjection;
@@ -55,6 +60,7 @@ import java.util.*;
public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
private static final String TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList";
+ private static final String TEMPLATE_FROM_USAGE_METHOD_BODY = "New Kotlin Method Body.kt";
/**
* Represents a concrete type or a set of types yet to be inferred from an expression.
@@ -360,7 +366,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
}
@Override
- public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
+ public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
assert file instanceof JetFile;
JetFile jetFile = (JetFile) file;
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache(jetFile).getBindingContext();
@@ -376,7 +382,8 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
ownerType = possibleOwnerTypes[0];
}
- ClassifierDescriptor ownerTypeDescriptor = ownerType.getConstructor().getDeclarationDescriptor();
+ final ClassifierDescriptor ownerTypeDescriptor = ownerType.getConstructor().getDeclarationDescriptor();
+ assert ownerTypeDescriptor != null;
PsiElement typeDeclaration = DescriptorToDeclarationUtil.getDeclaration(jetFile, ownerTypeDescriptor, context);
JetClass klass = (JetClass) typeDeclaration;
@@ -387,24 +394,28 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
}
String parametersString = StringUtil.join(parameterStrings,", ");
- boolean isUnit = returnType.isType() && isUnit(returnType.getType());
+ final boolean isUnit = returnType.isType() && isUnit(returnType.getType());
String returnTypeString = isUnit ? "" : ": Any";
String ownerTypeString;
String methodText;
JetNamedFunction func;
PsiElement owner;
+ final JetFile containingFile;
boolean isExtension = !klass.isWritable();
if (isExtension) { // create as extension function
ownerTypeString = renderType(ownerType);
methodText = String.format("fun %s.%s(%s)%s { }", ownerTypeString, methodName, parametersString, returnTypeString);
func = JetPsiFactory.createFunction(project, methodText);
- owner = file;
+ owner = containingFile = jetFile;
func = (JetNamedFunction) file.add(func);
} else { // create as method
methodText = String.format("fun %s(%s)%s { }", methodName, parametersString, returnTypeString);
func = JetPsiFactory.createFunction(project, methodText);
owner = klass;
+ PsiFile classContainingFile = klass.getContainingFile();
+ assert classContainingFile instanceof JetFile;
+ containingFile = (JetFile) classContainingFile;
JetClassBody classBody = klass.getBody();
assert classBody != null;
PsiElement rBrace = classBody.getRBrace();
@@ -439,7 +450,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
TypeParameterListExpression expression = setupTypeParameterListTemplate(builder, func, ownerType, parameterTypeExpressions, scope);
// the template built by TemplateBuilderImpl is ordered by element position, but we want types to be first, so hack it
- TemplateImpl template = (TemplateImpl) builder.buildInlineTemplate();
+ final TemplateImpl template = (TemplateImpl) builder.buildInlineTemplate();
ArrayList variables = template.getVariables();
for (int i = 0; i < parameters.size(); i++) {
Collections.swap(variables, i * 2, i * 2 + 1);
@@ -451,14 +462,47 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
// run the template
TemplateManager.getInstance(project).startTemplate(editor, template, new TemplateEditingAdapter() {
@Override
- public void templateFinished(Template template, boolean brokenOff) {
- // TODO: file templates
+ public void templateFinished(Template _, boolean brokenOff) {
+ int offset = template.getSegmentOffset(0);
+ JetNamedFunction func = PsiTreeUtil.findElementOfClassAtOffset(containingFile, offset, JetNamedFunction.class, false);
+ assert func != null;
+ setupFunctionBody(project, func, isUnit, ownerTypeDescriptor);
caretModel.moveToOffset(oldOffset);
}
});
}
+ private void setupFunctionBody(@NotNull Project project, @NotNull JetNamedFunction func, boolean isUnit,
+ @NotNull ClassifierDescriptor ownerTypeDescriptor
+ ) {
+ FileTemplate fileTemplate = FileTemplateManager.getInstance().getCodeTemplate(TEMPLATE_FROM_USAGE_METHOD_BODY);
+ Properties properties = new Properties();
+ if (isUnit) {
+ properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, "Unit");
+ } else {
+ JetTypeReference returnTypeRef = func.getReturnTypeRef();
+ assert returnTypeRef != null;
+ properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, returnTypeRef.getText());
+ }
+ properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFQName(ownerTypeDescriptor).getFqName());
+ properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, ownerTypeDescriptor.getName().getName());
+ properties.setProperty(FileTemplate.ATTRIBUTE_METHOD_NAME, methodName);
+
+ @NonNls String bodyText;
+ try {
+ bodyText = fileTemplate.getText(properties);
+ } catch (ProcessCanceledException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IncorrectOperationException("Failed to parse file template", e);
+ }
+ JetExpression newBodyExpression = JetPsiFactory.createFunctionBody(project, bodyText);
+ JetExpression oldBodyExpression = func.getBodyExpression();
+ assert oldBodyExpression != null;
+ oldBodyExpression.replace(newBodyExpression);
+ }
+
@NotNull
private static TypeExpression setupReturnTypeTemplate(@NotNull TemplateBuilder builder, @NotNull JetNamedFunction func,
@NotNull TypeOrExpressionThereof returnType) {