All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jetbrains.python.psi.impl.PyFunctionBuilder Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition python-community library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jetbrains.python.psi.impl;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.documentation.StructuredDocStringBase;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.regex.Pattern;

/**
 * @author yole
 */
public class PyFunctionBuilder {
  private static final String COMMENTS_BOUNDARY = "\"\"\"";
  private static final Pattern INDENT_REMOVE_PATTERN = Pattern.compile("^\\s+", Pattern.MULTILINE);
  private final String myName;
  private final List myParameters = new ArrayList();
  private final List myStatements = new ArrayList();
  private final List myDecorators = new ArrayList();
  private String myAnnotation = null;
  private String[] myDocStringLines = null;
  @NotNull
  private final Map myDecoratorValues = new HashMap();

  /**
   * Creates builder copying signature and doc from another one.
   *
   * @param source                  what to copy
   * @param decoratorsToCopyIfExist list of decorator names to be copied to new function.
   * @return builder configured by this function
   */
  @NotNull
  public static PyFunctionBuilder copySignature(@NotNull final PyFunction source, @NotNull final String... decoratorsToCopyIfExist) {
    final String name = source.getName();
    final PyFunctionBuilder functionBuilder = new PyFunctionBuilder((name != null) ? name : "");
    for (final PyParameter parameter : source.getParameterList().getParameters()) {
      final String parameterName = parameter.getName();
      if (parameterName != null) {
        functionBuilder.parameter(parameterName);
      }
    }
    final PyDecoratorList decoratorList = source.getDecoratorList();
    if (decoratorList != null) {
      for (final PyDecorator decorator : decoratorList.getDecorators()) {
        final String decoratorName = decorator.getName();
        if (decoratorName != null) {
          if (ArrayUtil.contains(decoratorName, decoratorsToCopyIfExist)) {
            functionBuilder.decorate(decoratorName);
          }
        }
      }
    }
    final String docString = source.getDocStringValue();
    if (docString != null) {
      functionBuilder.docString(docString);
    }
    return functionBuilder;
  }

  /**
   * Adds docstring to function. Provide doc with out of comment blocks.
   *
   *
   * @param docString doc
   */
  public void docString(@NotNull final String docString) {
    final String[] stringsToAdd = StringUtil.splitByLines(removeIndent(docString));
    if (myDocStringLines == null) {
      myDocStringLines = stringsToAdd;
    }
    else {
      myDocStringLines = ArrayUtil.mergeArrays(myDocStringLines, stringsToAdd);
    }
  }

  @NotNull
  private static String removeIndent(@NotNull final String string) {
    return INDENT_REMOVE_PATTERN.matcher(string).replaceAll("");
  }

  public PyFunctionBuilder(String name) {
    myName = name;
  }

  /**
   * Adds param and its type to doc
   * @param name param name
   * @param type param type
   * @param docStyle what docstyle to use to doc param type
   */
  @NotNull
  public PyFunctionBuilder parameterWithType(@NotNull final String name,
                                             @NotNull final String type,
                                             @NotNull final StructuredDocStringBase docStyle) {
    parameter(name);
    docString(docStyle.createParameterType(name, type));
    return this;
  }

  public PyFunctionBuilder parameter(String baseName) {
    String name = baseName;
    int uniqueIndex = 0;
    while (myParameters.contains(name)) {
      uniqueIndex++;
      name = baseName + uniqueIndex;
    }
    myParameters.add(name);
    return this;
  }

  public PyFunctionBuilder annotation(String text) {
    myAnnotation = text;
    return this;
  }

  public PyFunctionBuilder statement(String text) {
    myStatements.add(text);
    return this;
  }

  public PyFunction addFunction(PsiElement target, final LanguageLevel languageLevel) {
    return (PyFunction)target.add(buildFunction(target.getProject(), languageLevel));
  }

  public PyFunction addFunctionAfter(PsiElement target, PsiElement anchor, final LanguageLevel languageLevel) {
    return (PyFunction)target.addAfter(buildFunction(target.getProject(), languageLevel), anchor);
  }

  public PyFunction buildFunction(Project project, final LanguageLevel languageLevel) {
    PyElementGenerator generator = PyElementGenerator.getInstance(project);
    String text = buildText(project, generator, languageLevel);
    return generator.createFromText(languageLevel, PyFunction.class, text);
  }

  private String buildText(Project project, PyElementGenerator generator, LanguageLevel languageLevel) {
    StringBuilder builder = new StringBuilder();
    for (String decorator : myDecorators) {
      final StringBuilder decoratorAppender = builder.append('@' + decorator);
      if (myDecoratorValues.containsKey(decorator)) {
        final PyCallExpression fakeCall = generator.createCallExpression(languageLevel, "fakeFunction");
        fakeCall.getArgumentList().addArgument(generator.createStringLiteralFromString(myDecoratorValues.get(decorator)));
        decoratorAppender.append(fakeCall.getArgumentList().getText());
      }
      decoratorAppender.append("\n");
    }
    builder.append("def ");
    builder.append(myName).append("(");
    builder.append(StringUtil.join(myParameters, ", "));
    builder.append(")");
    if (myAnnotation != null) {
      builder.append(myAnnotation);
    }
    builder.append(":");
    List statements = myStatements.isEmpty() ? Collections.singletonList(PyNames.PASS) : myStatements;

    if (myDocStringLines != null) {
      final List comments = new ArrayList(myDocStringLines.length + 2);
      comments.add(COMMENTS_BOUNDARY);
      comments.addAll(Arrays.asList(myDocStringLines));
      comments.add(COMMENTS_BOUNDARY);
      statements = new ArrayList(statements);
      statements.addAll(0, comments);
    }

    final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getInstance(project).getCurrentSettings();
    int indentSize = codeStyleSettings.getIndentOptions(PythonFileType.INSTANCE).INDENT_SIZE;
    String indent = StringUtil.repeatSymbol(' ', indentSize);
    for (String statement : statements) {
      builder.append("\n").append(indent).append(statement);
    }
    return builder.toString();
  }

  /**
   * Adds decorator with argument
   *
   * @param decoratorName decorator name
   * @param value         its argument
   */
  public void decorate(@NotNull final String decoratorName, @NotNull final String value) {
    decorate(decoratorName);
    myDecoratorValues.put(decoratorName, value);
  }

  public void decorate(String decoratorName) {
    myDecorators.add(decoratorName);
  }

  @NotNull
  private static String getIndent(@NotNull final Project project) {
    final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getInstance(project).getCurrentSettings();
    final int indentSize = codeStyleSettings.getIndentOptions(PythonFileType.INSTANCE).INDENT_SIZE;
    return StringUtil.repeatSymbol(' ', indentSize);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy