Package org.intellij.erlang.quickfixes

Source Code of org.intellij.erlang.quickfixes.ErlangGenerateSpecFix

/*
* Copyright 2012-2014 Sergey Ignatov
*
* 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 org.intellij.erlang.quickfixes;

import com.intellij.codeInsight.template.TemplateBuilder;
import com.intellij.codeInsight.template.TemplateBuilderFactory;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.intellij.erlang.psi.*;
import org.intellij.erlang.psi.impl.ErlangElementFactory;
import org.intellij.erlang.types.ErlangExpressionType;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.List;

public class ErlangGenerateSpecFix extends ErlangQuickFixBase {
  public static final String NAME = "Generate spec";

  @NotNull
  @Override
  public String getFamilyName() {
    return NAME;
  }

  @Override
  public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
    ErlangFunction function = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), ErlangFunction.class, false);
    if (function != null) {
      Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
      if (editor != null) {
        generateSpec(editor, function);
      }
    }
  }

  public static void generateSpec(@NotNull Editor editor, @NotNull ErlangFunction function) {
    PsiFile containingFile = function.getContainingFile();
    if (!(containingFile instanceof ErlangFile)) return;
    ErlangFile file = (ErlangFile) containingFile;
    Project project = function.getProject();
    PsiElement spec = createSpecTemplate(function, file, project);
    runSpecTemplateEditor(editor, spec);
  }

  private static void runSpecTemplateEditor(final Editor editor, final PsiElement spec) {
    if (ApplicationManager.getApplication().isUnitTestMode()) return;
    ApplicationManager.getApplication().invokeLater(new Runnable() {
      @Override
      public void run() {
        CommandProcessor.getInstance().executeCommand(editor.getProject(), new Runnable() {
          @Override
          public void run() {
            ApplicationManager.getApplication().runWriteAction(new Runnable() {
              @Override
              public void run() {
                TemplateBuilder templateBuilder = TemplateBuilderFactory.getInstance().createTemplateBuilder(spec);
                Collection<ErlangTopType> types = PsiTreeUtil.findChildrenOfType(spec, ErlangTopType.class);
                for (ErlangTopType type : types) {
                  templateBuilder.replaceElement(type, type.getText());
                }
                templateBuilder.run(editor, false);
              }
            });
          }
        }, "Generate spec template editor", null);
      }
    });
  }

  private static PsiElement createSpecTemplate(ErlangFunction function, ErlangFile file, Project project) {
    StringBuilder specText = new StringBuilder();
    specText.append(function.getName())
            .append('(').append(computeArgsTypeSpecs(function)).append(')')
            .append(" -> ")
            .append(getTypeString(computeReturnType(function)))
            .append('.');
    PsiElement spec = file.addBefore(ErlangElementFactory.createSpecFromText(project, specText.toString()), function);
    file.addBefore(ErlangElementFactory.createLeafFromText(project, "\n"), function);
    return spec;
  }

  private static String computeArgsTypeSpecs(ErlangFunction function) {
    StringBuilder argTypes = new StringBuilder();
    int arity = function.getArity();
    for (int i = 0; i < arity; i++) {
      ErlangExpressionType argType = computeArgumentType(function, i);
      argTypes.append(getTypeString(argType)).append(", ");
    }
    if (arity != 0) {
      argTypes.setLength(argTypes.length() - 2);
    }
    return argTypes.toString();
  }

  private static String getTypeString(ErlangExpressionType t) {
    return t == ErlangExpressionType.UNKNOWN ? "any()" : t.getName().toLowerCase() + "()";
  }

  private static ErlangExpressionType computeReturnType(ErlangFunction function) {
    List<ErlangFunctionClause> clauses = function.getFunctionClauseList();
    List<ErlangExpression> lastExpressions = ContainerUtil.newArrayListWithCapacity(clauses.size());
    for (ErlangFunctionClause clause : clauses) {
      ErlangClauseBody clauseBody = clause.getClauseBody();
      if (clauseBody != null) {
        ErlangExpression lastExpressionInClause = ContainerUtil.getLastItem(clauseBody.getExpressionList());
        ContainerUtil.addIfNotNull(lastExpressions, lastExpressionInClause);
      }
    }
    return computeCommonType(lastExpressions);
  }

  private static ErlangExpressionType computeArgumentType(ErlangFunction function, int argumentIdx) {
    List<ErlangExpression> argumentPatterns = getArgumentPatterns(function, argumentIdx);
    return computeCommonType(argumentPatterns);
  }

  private static List<ErlangExpression> getArgumentPatterns(ErlangFunction function, int argumentIdx) {
    List<ErlangFunctionClause> clauses = function.getFunctionClauseList();
    List<ErlangExpression> argumentPatterns = ContainerUtil.newArrayListWithCapacity(clauses.size());
    for (ErlangFunctionClause clause : clauses) {
      ErlangArgumentDefinitionList argDefList = clause.getArgumentDefinitionList();
      List<ErlangArgumentDefinition> clauseArgs = argDefList.getArgumentDefinitionList();
      ErlangArgumentDefinition argDef = argumentIdx < clauseArgs.size() ? clauseArgs.get(argumentIdx) : null;
      ContainerUtil.addIfNotNull(argumentPatterns, argDef != null ? argDef.getExpression() : null);
    }
    return argumentPatterns;
  }

  private static ErlangExpressionType computeCommonType(List<ErlangExpression> expressions) {
    List<ErlangExpressionType> types = ContainerUtil.map(expressions, new Function<ErlangExpression, ErlangExpressionType>() {
      @Override
      public ErlangExpressionType fun(ErlangExpression e) {
        return ErlangExpressionType.create(e);
      }
    });
    //TODO compute common type
    return types.isEmpty() ? ErlangExpressionType.UNKNOWN : types.get(0);
  }

}
TOP

Related Classes of org.intellij.erlang.quickfixes.ErlangGenerateSpecFix

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.