package org.jetbrains.plugins.clojure.runner.console;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.HyperlinkInfo;
import com.intellij.execution.filters.OpenFileHyperlinkInfo;
import com.intellij.execution.testframework.stacktrace.DiffHyperlink;
import com.intellij.ide.DataManager;
import com.intellij.ide.util.EditSourceUtil;
import com.intellij.ide.util.PsiElementListCellRenderer;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.Navigatable;
import com.intellij.psi.*;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author ilyas
*/
public class ClojureFilter implements Filter {
private final Project myProject;
private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.clojure.runner.console.ClojureFilter");
private static final Pattern FILE_PATTERN = Pattern.compile(".*\\((\\w*\\.clj):(\\d+)(:(\\d+))?\\)(\\s|.)*");
private static final Pattern ASSERT_PATTERN = Pattern.compile(" actual: (\\(not \\(= \"(.*)\" \"(.*)\"\\)\\))\n");
public ClojureFilter(Project project) {
myProject = project;
}
public Result applyFilter(String line, int entireLength) {
Result result = matchFileName(line, entireLength);
return result == null ? matchComparisonFailure(line, entireLength) : result;
}
private Result matchFileName(String line, int entireLength) {
try {
final Matcher matcher = FILE_PATTERN.matcher(line);
if (matcher.matches()) {
final String fileName = matcher.group(1);
final int lineNumber = Integer.parseInt(matcher.group(2));
String colGroup = matcher.group(4);
boolean hasColumn = !StringUtil.isEmpty(colGroup);
final int textStartOffset = entireLength - line.length();
final PsiFile[] psiFiles = FilenameIndex.getFilesByName(myProject, fileName, GlobalSearchScope.allScope(myProject));
if (psiFiles.length == 0) return null;
final HyperlinkInfo info = psiFiles.length == 1 ?
new OpenFileHyperlinkInfo(myProject, psiFiles[0].getVirtualFile(), lineNumber - 1, hasColumn ? Integer.parseInt(colGroup) - 1 : 0) :
new MyHyperlinkInfo(psiFiles);
return new Result(textStartOffset + matcher.start(1), textStartOffset + matcher.end(hasColumn ? 4 : 2), info);
}
}
catch (NumberFormatException e) {
LOG.debug(e);
}
return null;
}
private static Result matchComparisonFailure(String line, int entireLength) {
Matcher matcher = ASSERT_PATTERN.matcher(line);
if (matcher.matches()) {
String expected = StringUtil.replace(matcher.group(2), "\\n", "\n");
String actual = StringUtil.replace(matcher.group(3), "\\n", "\n");
if (expected.contains("\n") && actual.contains("\n")) {
final int textStartOffset = entireLength - line.length();
DiffHyperlink diffHyperlink = new DiffHyperlink(expected, actual, null);
DiffHyperlink.DiffHyperlinkInfo hyperlinkInfo = diffHyperlink.new DiffHyperlinkInfo();
return new Result(textStartOffset + matcher.start(1), textStartOffset + matcher.end(1), hyperlinkInfo);
}
}
return null;
}
private static class MyHyperlinkInfo implements HyperlinkInfo {
private final PsiFile[] myPsiFiles;
public MyHyperlinkInfo(final PsiFile[] psiFiles) {
myPsiFiles = psiFiles;
}
public void navigate(final Project project) {
DefaultPsiElementListCellRenderer renderer = new DefaultPsiElementListCellRenderer();
final JList list = new JList(myPsiFiles);
list.setCellRenderer(renderer);
renderer.installSpeedSearch(list);
final Runnable runnable = new Runnable() {
public void run() {
int[] ids = list.getSelectedIndices();
if (ids == null || ids.length == 0) return;
Object[] selectedElements = list.getSelectedValues();
for (Object element : selectedElements) {
Navigatable descriptor = EditSourceUtil.getDescriptor((PsiElement) element);
if (descriptor != null && descriptor.canNavigate()) {
descriptor.navigate(true);
}
}
}
};
final Editor editor = PlatformDataKeys.EDITOR.getData(DataManager.getInstance().getDataContext());
new PopupChooserBuilder(list).
setTitle("Choose file").
setItemChoosenCallback(runnable).
createPopup().showInBestPositionFor(editor);
}
}
private static class DefaultPsiElementListCellRenderer extends PsiElementListCellRenderer {
public String getElementText(final PsiElement element) {
return element.getContainingFile().getName();
}
@Nullable
protected String getContainerText(final PsiElement element, final String name) {
final PsiDirectory parent = ((PsiFile) element).getParent();
if (parent == null) return null;
final PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(parent);
if (psiPackage == null) return null;
return "(" + psiPackage.getQualifiedName() + ")";
}
protected int getIconFlags() {
return 0;
}
}
}