// Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).
package com.twitter.intellij.pants.quickfix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiParserFacade;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.psi.*;
import com.twitter.intellij.pants.PantsBundle;
import com.twitter.intellij.pants.model.PantsTargetAddress;
import com.twitter.intellij.pants.util.PantsPsiUtil;
import com.twitter.intellij.pants.util.PantsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AddPantsTargetDependencyFix extends PantsQuickFix {
protected final PantsTargetAddress myAddress;
protected final PantsTargetAddress myAddressToAdd;
public AddPantsTargetDependencyFix(@NotNull PantsTargetAddress address, @NotNull PantsTargetAddress addressToAdd) {
myAddress = address;
myAddressToAdd = addressToAdd;
}
@NotNull
@Override
public String getName() {
return PantsBundle.message("quick.fix.add.target.dependency.description");
}
@Override
public boolean startInWriteAction() {
return true;
}
@NotNull
@Override
public String getText() {
return PantsBundle.message("quick.fix.add.target.dependency.text", myAddressToAdd, myAddress);
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
final Module module = ModuleUtil.findModuleForPsiElement(file);
return module != null && PantsUtil.findBUILDFileForModule(module) != null;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
invoke(project, null, descriptor.getPsiElement().getContainingFile());
}
@Override
public void invoke(@NotNull Project project, @Nullable Editor editor, @NotNull PsiFile psiFile) throws IncorrectOperationException {
final Module module = ModuleUtil.findModuleForPsiElement(psiFile);
final VirtualFile buildFile = module != null ? PantsUtil.findBUILDFileForModule(module).orElse(null) : null;
final PsiFile psiBuildFile = buildFile != null ? PsiManager.getInstance(project).findFile(buildFile) : null;
if (psiBuildFile != null && StringUtil.isNotEmpty(myAddress.getTargetName())) {
doInsert(psiBuildFile, myAddress.getTargetName(), myAddressToAdd);
}
}
public void doInsert(
@NotNull PsiFile buildFile,
@NotNull final String targetName,
@NotNull PantsTargetAddress addressToAdd
) throws IncorrectOperationException {
final PyCallExpression targetDefinitionExpression = PantsPsiUtil.findTargets(buildFile).get(targetName);
if (targetDefinitionExpression == null) {
return;
}
final Project project = buildFile.getProject();
final PyElementGenerator generator = PyElementGenerator.getInstance(project);
final String targetAddressStringToAdd = addressToAdd.toString();
final PyExpression dependenciesArgument = targetDefinitionExpression.getKeywordArgument("dependencies");
if (dependenciesArgument == null) {
final PyKeywordArgument keywordArgument =
generator.createKeywordArgument(LanguageLevel.forElement(buildFile), "dependencies", "['"+ targetAddressStringToAdd + "']");
targetDefinitionExpression.addArgument(keywordArgument);
} else if (dependenciesArgument instanceof PyListLiteralExpression) {
PyExpression position = null;
// we assume all elements are sorted.
for (PyExpression expression : ((PyListLiteralExpression)dependenciesArgument).getElements()) {
if (expression instanceof PyStringLiteralExpression &&
targetAddressStringToAdd.compareTo(((PyStringLiteralExpression)expression).getStringValue()) < 0) {
// found a position to insert
break;
}
position = expression;
}
final PyStringLiteralExpression literalToAdd = generator.createStringLiteralAlreadyEscaped("'" + targetAddressStringToAdd + "'");
if (position != null) {
final PsiElement newLine = PsiParserFacade.SERVICE.getInstance(project).createWhiteSpaceFromText("\n");
final PsiElement addedLiteral = dependenciesArgument.addAfter(literalToAdd, position);
dependenciesArgument.getNode().addChild(newLine.getNode(), addedLiteral.getNode());
} else {
dependenciesArgument.add(literalToAdd);
}
CodeStyleManager.getInstance(project).reformat(dependenciesArgument);
}
FileDocumentManager.getInstance().saveAllDocuments(); // dump VFS to FS before refreshing
PantsUtil.refreshAllProjects(project);
}
}