package com.intellij.javascript.karma.filter;
import com.intellij.execution.filters.*;
import com.intellij.lang.javascript.modules.NodeModuleUtil;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class KarmaSourceMapStacktraceFilter extends AbstractFileHyperlinkFilter implements DumbAware {
private static final String SEPARATOR = " <- ";
private static final String WEBPACK_URL_PREFIX = "webpack:///";
public static final KarmaSourceMapStacktraceFinder FINDER = new KarmaSourceMapStacktraceFinder();
private final AbstractFileHyperlinkFilter myBaseFilter;
public KarmaSourceMapStacktraceFilter(@NotNull Project project,
@Nullable String baseDir,
@NotNull AbstractFileHyperlinkFilter baseFilter) {
super(project, baseDir);
myBaseFilter = baseFilter;
}
@NotNull
@Override
public List<FileHyperlinkRawData> parse(@NotNull String line) {
if (line.contains(SEPARATOR)) {
List<FileHyperlinkRawData> list = FINDER.find(line);
if (!list.isEmpty()) {
return list;
}
}
return myBaseFilter.parse(line);
}
@Nullable
@Override
public VirtualFile findFile(@NotNull String filePath) {
VirtualFile file = super.findFile(filePath);
if (file == null && filePath.startsWith("/tmp/")) {
return super.findFile(StringUtil.trimStart(filePath, "/tmp/"));
}
return file;
}
public static class KarmaSourceMapStacktraceFinder implements FileHyperlinkRawDataFinder {
private static final Pattern[] PATTERNS = new Pattern[] {
Pattern.compile("^\\s*at\\s.*\\(([^(]*:\\d+:\\d+) <- .*"),
Pattern.compile("^\\s*at\\s+([^\\s(].*:\\d+:\\d+) <- .*"),
Pattern.compile("^[^@]*[^/]@(.*:\\d+:\\d+) <- .*"),
Pattern.compile("^\\s*([^\\s].*:\\d+:\\d+) <- .*")
};
private static final PatternBasedFileHyperlinkRawDataFinder INNER_FINDER = new PatternBasedFileHyperlinkRawDataFinder(
new PatternHyperlinkFormat[] {
new PatternHyperlinkFormat(
Pattern.compile("(.*):(\\d+):(\\d+)"), false, false,
PatternHyperlinkPart.PATH, PatternHyperlinkPart.LINE, PatternHyperlinkPart.COLUMN
)
}
);
@NotNull
@Override
public List<FileHyperlinkRawData> find(@NotNull String line) {
for (Pattern pattern : PATTERNS) {
Matcher matcher = pattern.matcher(line);
if (matcher.matches()) {
List<FileHyperlinkRawData> result = ContainerUtil.newArrayList();
for (int i = 1; i <= matcher.groupCount(); i++) {
String originalLink = matcher.group(i);
String normalizedLink = normalizeLinkPrefix(originalLink);
List<FileHyperlinkRawData> list = INNER_FINDER.find(normalizedLink);
for (FileHyperlinkRawData data : list) {
result.add(new FileHyperlinkRawData(data.getFilePath(),
data.getDocumentLine(),
data.getDocumentColumn(),
matcher.start(i),
matcher.start(i) + originalLink.length()));
}
}
return result;
}
}
return Collections.emptyList();
}
@NotNull
private static String normalizeLinkPrefix(@NotNull String link) {
if (link.startsWith(WEBPACK_URL_PREFIX)) {
link = link.substring(WEBPACK_URL_PREFIX.length());
if (link.startsWith("~/")) {
link = NodeModuleUtil.NODE_MODULES + link.substring(1);
}
}
return link;
}
}
}