package org.elixir_lang.mix.runner.exunit;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.actions.RunConfigurationProducer;
import com.intellij.ide.projectView.impl.ProjectRootsUtil;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkTypeId;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import org.elixir_lang.psi.ElixirFile;
import org.elixir_lang.sdk.ElixirSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
public class MixExUnitRunConfigurationProducer extends RunConfigurationProducer<MixExUnitRunConfiguration> {
/*
* CONSTANTS
*/
private static int UNKNOWN_LINE = -1;
/*
* Constructors
*/
public MixExUnitRunConfigurationProducer() {
super(MixExUnitRunConfigurationType.getInstance());
}
/*
* Instance Methods
*/
@Override
protected final boolean setupConfigurationFromContext(MixExUnitRunConfiguration runConfig, ConfigurationContext context, Ref<PsiElement> ref) {
PsiElement location = ref.get();
return location != null && location.isValid() &&
setupConfigurationFromContextImpl(runConfig, location);
}
private boolean setupConfigurationFromContextImpl(@NotNull MixExUnitRunConfiguration configuration,
@NotNull PsiElement psiElement) {
boolean contextApplicable = false;
if (psiElement instanceof PsiDirectory) {
PsiDirectory psiDirectory = (PsiDirectory) psiElement;
Module module = ModuleUtilCore.findModuleForPsiElement(psiDirectory);
Sdk sdk;
if (module != null) {
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
sdk = moduleRootManager.getSdk();
} else {
ProjectRootManager projectRootManager = ProjectRootManager.getInstance(psiDirectory.getProject());
sdk = projectRootManager.getProjectSdk();
}
SdkTypeId sdkTypeId = null;
if (sdk != null) {
sdkTypeId = sdk.getSdkType();
}
if ((sdkTypeId == null || sdkTypeId.equals(ElixirSdkType.getInstance())) &&
ProjectRootsUtil.isInTestSource(psiDirectory.getVirtualFile(), psiDirectory.getProject())) {
String basePath = psiElement.getProject().getBasePath();
String workingDirectory = workingDirectory(psiElement, basePath);
configuration.setWorkingDirectory(workingDirectory);
PsiDirectory dir = (PsiDirectory) psiElement;
configuration.setName(configurationName(dir, workingDirectory, basePath));
configuration.setProgramParameters(programParameters(dir, workingDirectory));
contextApplicable = true;
}
} else {
PsiFile containingFile = psiElement.getContainingFile();
if (!(containingFile instanceof ElixirFile || containingFile instanceof PsiDirectory)) return false;
if (ProjectRootsUtil.isInTestSource(containingFile)) {
String basePath = psiElement.getProject().getBasePath();
String workingDirectory = workingDirectory(psiElement, basePath);
configuration.setWorkingDirectory(workingDirectory);
int lineNumber = lineNumber(psiElement);
configuration.setName(configurationName(containingFile, lineNumber, workingDirectory, basePath));
configuration.setProgramParameters(programParameters(containingFile, lineNumber, workingDirectory));
contextApplicable = true;
}
}
return contextApplicable;
}
@Override
public final boolean isConfigurationFromContext(MixExUnitRunConfiguration runConfig, ConfigurationContext context) {
PsiElement location = context.getPsiLocation();
return location != null && location.isValid() &&
isConfigurationFromContextImpl(runConfig, location);
}
private boolean isConfigurationFromContextImpl(@NotNull MixExUnitRunConfiguration configuration,
@NotNull PsiElement psiElement) {
PsiFile containingFile = psiElement.getContainingFile();
VirtualFile vFile = containingFile != null ? containingFile.getVirtualFile() : null;
if (vFile == null) return false;
int lineNumber = lineNumber(psiElement);
String workingDirectory = configuration.getWorkingDirectory();
return StringUtil.equals(
configuration.getName(),
configurationName(
containingFile,
lineNumber,
workingDirectory,
psiElement.getProject().getBasePath()
)
) &&
StringUtil.equals(
configuration.getProgramParameters(),
programParameters(containingFile, lineNumber, workingDirectory)
);
}
private int lineNumber(@NotNull PsiElement psiElement) {
PsiFile containingFile = psiElement.getContainingFile();
Project project = containingFile.getProject();
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
Document document = psiDocumentManager.getDocument(containingFile);
int documentLineNumber = 0;
if (document != null) {
int textOffset = psiElement.getTextOffset();
documentLineNumber = document.getLineNumber(textOffset);
}
int lineNumber;
if (documentLineNumber == 0) {
lineNumber = UNKNOWN_LINE;
} else {
lineNumber = documentLineNumber + 1;
}
return lineNumber;
}
private String configurationName(PsiFileSystemItem file,
@Nullable String workingDirectory,
@Nullable String basePath) {
String filePath = file.getVirtualFile().getPath();
String suffix = null;
if (workingDirectory != null) {
String prefix = workingDirectory + File.separator;
if (filePath.startsWith(prefix)) {
suffix = filePath.substring(prefix.length());
}
if (basePath != null && !workingDirectory.equals(basePath) && workingDirectory.startsWith(basePath)) {
String otpAppName = new File(workingDirectory).getName();
suffix = otpAppName + " " + suffix;
}
}
if (suffix == null) {
suffix = file.getName();
}
return "Mix ExUnit " + suffix;
}
private String configurationName(PsiFileSystemItem file,
int lineNumber,
@Nullable String workingDirectory,
@Nullable String basePath) {
if (lineNumber == UNKNOWN_LINE) {
return configurationName(file, workingDirectory, basePath);
} else {
return configurationName(file, workingDirectory, basePath) + ":" + lineNumber;
}
}
@NotNull
private String programParameters(@NotNull PsiFileSystemItem item, @Nullable String workingDirectory) {
return programParameters(item, UNKNOWN_LINE, workingDirectory);
}
@NotNull
private String programParameters(@NotNull PsiFileSystemItem item,
int lineNumber,
@Nullable String workingDirectory) {
String path = item.getVirtualFile().getPath();
String relativePath = path;
if (workingDirectory != null) {
String prefix = workingDirectory + File.separator;
if (path.startsWith(prefix)) {
relativePath = path.substring(prefix.length());
}
}
String programParameters = relativePath;
if (lineNumber != UNKNOWN_LINE) {
programParameters += ":" + lineNumber;
}
return programParameters;
}
@Nullable
private static String workingDirectory(@NotNull PsiDirectory directory, @Nullable String basePath) {
String workingDirectory;
if (directory.findFile("mix.exs") != null) {
workingDirectory = directory.getVirtualFile().getPath();
} else {
PsiDirectory parent = directory.getParent();
if (parent != null) {
workingDirectory = workingDirectory(parent, basePath);
} else {
workingDirectory = basePath;
}
}
return workingDirectory;
}
@Nullable
private static String workingDirectory(@NotNull PsiElement element, @Nullable String basePath) {
String workingDirectory;
if (element instanceof PsiDirectory) {
workingDirectory = workingDirectory((PsiDirectory) element, basePath);
} else if (element instanceof PsiFile) {
workingDirectory = workingDirectory((PsiFile) element, basePath);
} else {
workingDirectory = workingDirectory(element.getContainingFile(), basePath);
}
return workingDirectory;
}
@Nullable
private static String workingDirectory(@NotNull PsiFile file, @Nullable String basePath) {
return workingDirectory(file.getContainingDirectory(), basePath);
}
}