annotate stacktraces. for file-level now

This commit is contained in:
irengrig
2011-08-08 16:00:39 +04:00
parent 0002ba9f31
commit 51e59817ee
19 changed files with 900 additions and 101 deletions
@@ -15,6 +15,7 @@
*/
package com.intellij.execution.filters;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.colors.CodeInsightColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.TextAttributes;
@@ -23,14 +24,13 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotation;
import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationImpl;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.BeforeAfter;
import com.intellij.util.Consumer;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -38,12 +38,14 @@ import org.jetbrains.annotations.Nullable;
import java.awt.*;
public class ExceptionFilter implements Filter, DumbAware {
public class ExceptionFilter implements Filter, DumbAware, FilterMixin {
public static final Color CHANGED_BACKGROUND = new Color(188, 237, 201);
private final Project myProject;
@NonNls private static final String AT = "at";
private static final String AT_PREFIX = AT + " ";
private static final String STANDALONE_AT = " " + AT + " ";
private static final TextAttributes HYPERLINK_ATTRIBUTES = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES);
private final GlobalSearchScope mySearchScope;
public ExceptionFilter(@NotNull final Project project) {
@@ -57,7 +59,7 @@ public class ExceptionFilter implements Filter, DumbAware {
}
@Nullable
static Trinity<String, String, TextRange> parseExceptionLine(final String line) {
static Trinity<TextRange, TextRange, TextRange> parseExceptionLine(final String line) {
int atIndex;
if (line.startsWith(AT_PREFIX)){
atIndex = 0;
@@ -74,76 +76,178 @@ public class ExceptionFilter implements Filter, DumbAware {
if (lparenthIndex < 0) return null;
final int lastDotIndex = line.lastIndexOf('.', lparenthIndex);
if (lastDotIndex < 0 || lastDotIndex < atIndex) return null;
String className = line.substring(atIndex + AT.length() + 1, lastDotIndex).trim();
String methodName = line.substring(lastDotIndex + 1, lparenthIndex).trim();
final int rparenthIndex = line.indexOf(')', lparenthIndex);
if (rparenthIndex < 0) return null;
return Trinity.create(className, methodName, new TextRange(lparenthIndex, rparenthIndex));
// class, method, link
return Trinity.create(adjustedRange(line, atIndex + AT.length() + 1, lastDotIndex),
adjustedRange(line, lastDotIndex + 1, lparenthIndex), new TextRange(lparenthIndex, rparenthIndex));
}
private static TextRange adjustedRange(final String line, final int start, final int end) {
String sub = line.substring(start, end);
return new TextRange(start, end - spacesEnd(sub));
}
private static int spacesStart(final String s) {
int cnt = 0;
for (int i = 0; i < s.length(); i++) {
final char c = s.charAt(i);
if (! Character.isSpaceChar(c)) return cnt;
++ cnt;
}
return 0;
}
private static int spacesEnd(final String s) {
int cnt = 0;
for (int i = s.length() - 1; i >= 0; i--) {
final char c = s.charAt(i);
if (! Character.isSpaceChar(c)) return cnt;
++ cnt;
}
return 0;
}
// todo do not work internal code
@Override
public void applyHeavyFilter(final String line, final int entireLength, int lineNumber, Consumer<AdditionalHighlight> consumer) {
final MyWorker worker = new MyWorker();
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
worker.execute(line, entireLength);
}
});
if (worker.getResult() != null) {
// find method range
final PsiMethod[] methodsByName = worker.getPsiClass().findMethodsByName(worker.getMethod(), false);
// todo also go up etc. now just take first
if (methodsByName.length > 0) {
}
VcsContentAnnotation.Details details = VcsContentAnnotationImpl.getInstance(myProject)
.annotateLine(worker.getFile().getVirtualFile(), new BeforeAfter<Integer>(-1, -1), lineNumber);
if (details != null) {
if (details.isFileChanged()) {
final int textStartOffset = entireLength - line.length();
int idx = line.indexOf(':', worker.getInfo().getThird().getStartOffset());
int endIdx = idx == -1 ? worker.getInfo().getThird().getEndOffset() : idx;
consumer.consume(new AdditionalHighlight(textStartOffset + worker.getInfo().getThird().getStartOffset() + 1,
textStartOffset + endIdx) {
@Override
public TextAttributes getTextAttributes(@Nullable TextAttributes source) {
if (source == null) {
TextAttributes atts =
EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.CLASS_NAME_ATTRIBUTES).clone();
atts.setBackgroundColor(CHANGED_BACKGROUND);
return atts;
}
TextAttributes clone = source.clone();
clone.setBackgroundColor(CHANGED_BACKGROUND);
return clone;
}
});
}
// todo also other
}
}
}
public Result applyFilter(final String line, final int textEndOffset) {
final Trinity<String, String, TextRange> info = parseExceptionLine(line);
if (info == null) {
return null;
}
String className = info.first;
final int dollarIndex = className.indexOf('$');
if (dollarIndex >= 0){
className = className.substring(0, dollarIndex);
}
final int lparenthIndex = info.third.getStartOffset();
final int rparenthIndex = info.third.getEndOffset();
final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex).trim();
final int colonIndex = fileAndLine.lastIndexOf(':');
if (colonIndex < 0) return null;
final String lineString = fileAndLine.substring(colonIndex + 1);
try{
final int lineNumber = Integer.parseInt(lineString);
final PsiManager manager = PsiManager.getInstance(myProject);
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(manager.getProject());
PsiClass aClass = psiFacade.findClass(className, mySearchScope);
if (aClass == null) {
aClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject));
if (aClass == null) {//try to find class according to all dollars in package name
aClass = psiFacade.findClass(info.first, GlobalSearchScope.allScope(myProject));
}
if (aClass == null) return null;
}
final PsiFile file = (PsiFile) aClass.getContainingFile().getNavigationElement();
if (file == null) return null;
/*
IDEADEV-4976: Some scramblers put something like SourceFile mock instead of real class name.
final String filePath = fileAndLine.substring(0, colonIndex).replace('/', File.separatorChar);
final int slashIndex = filePath.lastIndexOf(File.separatorChar);
final String shortFileName = slashIndex < 0 ? filePath : filePath.substring(slashIndex + 1);
if (!file.getName().equalsIgnoreCase(shortFileName)) return null;
*/
final int textStartOffset = textEndOffset - line.length();
final int highlightStartOffset = textStartOffset + lparenthIndex + 1;
final int highlightEndOffset = textStartOffset + rparenthIndex;
VirtualFile virtualFile = file.getVirtualFile();
final OpenFileHyperlinkInfo linkInfo = new OpenFileHyperlinkInfo(myProject, virtualFile, lineNumber - 1);
TextAttributes attributes = HYPERLINK_ATTRIBUTES.clone();
if (!ProjectRootManager.getInstance(myProject).getFileIndex().isInContent(virtualFile)) {
Color color = UIUtil.getInactiveTextColor();
attributes.setForegroundColor(color);
attributes.setEffectColor(color);
}
return new Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes);
}
catch(NumberFormatException e){
return null;
}
final MyWorker worker = new MyWorker();
worker.execute(line, textEndOffset);
return worker.getResult();
}
private class MyWorker {
private Result myResult;
private PsiClass myClass;
private PsiFile myFile;
private String myMethod;
private Trinity<TextRange,TextRange,TextRange> myInfo;
public void execute(final String line, final int textEndOffset) {
myInfo = parseExceptionLine(line);
if (myInfo == null) {
return;
}
myMethod = myInfo.getSecond().substring(line);
String className = myInfo.first.substring(line).trim();
final int dollarIndex = className.indexOf('$');
if (dollarIndex >= 0){
className = className.substring(0, dollarIndex);
}
final int lparenthIndex = myInfo.third.getStartOffset();
final int rparenthIndex = myInfo.third.getEndOffset();
final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex).trim();
final int colonIndex = fileAndLine.lastIndexOf(':');
if (colonIndex < 0) return;
final String lineString = fileAndLine.substring(colonIndex + 1);
try{
final int lineNumber = Integer.parseInt(lineString);
final PsiManager manager = PsiManager.getInstance(myProject);
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(manager.getProject());
myClass = psiFacade.findClass(className, mySearchScope);
if (myClass == null) {
myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject));
if (myClass == null) {//try to find class according to all dollars in package name
myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject));
}
if (myClass == null) return;
}
myFile = (PsiFile) myClass.getContainingFile().getNavigationElement();
if (myFile == null) return;
/*
IDEADEV-4976: Some scramblers put something like SourceFile mock instead of real class name.
final String filePath = fileAndLine.substring(0, colonIndex).replace('/', File.separatorChar);
final int slashIndex = filePath.lastIndexOf(File.separatorChar);
final String shortFileName = slashIndex < 0 ? filePath : filePath.substring(slashIndex + 1);
if (!file.getName().equalsIgnoreCase(shortFileName)) return null;
*/
final int textStartOffset = textEndOffset - line.length();
final int highlightStartOffset = textStartOffset + lparenthIndex + 1;
final int highlightEndOffset = textStartOffset + rparenthIndex;
VirtualFile virtualFile = myFile.getVirtualFile();
final OpenFileHyperlinkInfo linkInfo = new OpenFileHyperlinkInfo(myProject, virtualFile, lineNumber - 1);
TextAttributes attributes = HYPERLINK_ATTRIBUTES.clone();
if (!ProjectRootManager.getInstance(myProject).getFileIndex().isInContent(virtualFile)) {
Color color = UIUtil.getInactiveTextColor();
attributes.setForegroundColor(color);
attributes.setEffectColor(color);
}
myResult = new Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes);
}
catch(NumberFormatException e){
//
}
}
public Result getResult() {
return myResult;
}
public PsiClass getPsiClass() {
return myClass;
}
public String getMethod() {
return myMethod;
}
public PsiFile getFile() {
return myFile;
}
public Trinity<TextRange, TextRange, TextRange> getInfo() {
return myInfo;
}
}
}