mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-186244 Analyse Stack Trace does not understand jstack -F traces
This commit is contained in:
+7
-11
@@ -21,7 +21,6 @@ import com.intellij.openapi.editor.markup.EffectType;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.Trinity;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
@@ -65,8 +64,8 @@ public class ExceptionExFilterFactory implements ExceptionFilterFactory {
|
||||
final int startOffset,
|
||||
int startLineNumber,
|
||||
@NotNull final Consumer<AdditionalHighlight> consumer) {
|
||||
Map<String, Trinity<TextRange, TextRange, TextRange>> visited = new THashMap<>();
|
||||
final Trinity<TextRange, TextRange, TextRange> emptyInfo = Trinity.create(null, null, null);
|
||||
Map<String, ExceptionWorker.ParsedLine> visited = new THashMap<>();
|
||||
ExceptionWorker.ParsedLine emptyInfo = new ExceptionWorker.ParsedLine(TextRange.EMPTY_RANGE, TextRange.EMPTY_RANGE, TextRange.EMPTY_RANGE, null, -1);
|
||||
|
||||
final ExceptionWorker worker = new ExceptionWorker(myCache);
|
||||
for (int i = 0; i < copiedFragment.getLineCount(); i++) {
|
||||
@@ -75,11 +74,11 @@ public class ExceptionExFilterFactory implements ExceptionFilterFactory {
|
||||
|
||||
String lineText = copiedFragment.getText(new TextRange(lineStartOffset, lineEndOffset));
|
||||
if (!lineText.contains(".java:")) continue;
|
||||
Trinity<TextRange, TextRange, TextRange> info = visited.get(lineText);
|
||||
ExceptionWorker.ParsedLine info = visited.get(lineText);
|
||||
if (info == emptyInfo) continue;
|
||||
|
||||
if (info == null) {
|
||||
info = ReadAction.compute(() -> doparse(emptyInfo, worker, lineEndOffset, lineText));
|
||||
info = ReadAction.compute(() -> doParse(worker, lineEndOffset, lineText));
|
||||
visited.put(lineText, info == null ? emptyInfo : info);
|
||||
if (info == null) {
|
||||
continue;
|
||||
@@ -87,7 +86,7 @@ public class ExceptionExFilterFactory implements ExceptionFilterFactory {
|
||||
}
|
||||
int off = startOffset + lineStartOffset;
|
||||
final Color color = UIUtil.getInactiveTextColor();
|
||||
consumer.consume(new AdditionalHighlight(off + info.first.getStartOffset(), off + info.second.getEndOffset()) {
|
||||
consumer.consume(new AdditionalHighlight(off + info.classFqnRange.getStartOffset(), off + info.methodNameRange.getEndOffset()) {
|
||||
@NotNull
|
||||
@Override
|
||||
public TextAttributes getTextAttributes(@Nullable TextAttributes source) {
|
||||
@@ -97,9 +96,7 @@ public class ExceptionExFilterFactory implements ExceptionFilterFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private Trinity<TextRange, TextRange, TextRange> doparse(Trinity<TextRange, TextRange, TextRange> emptyInfo,
|
||||
ExceptionWorker worker,
|
||||
int lineEndOffset, String lineText) {
|
||||
private static ExceptionWorker.ParsedLine doParse(ExceptionWorker worker, int lineEndOffset, String lineText) {
|
||||
Result result = worker.execute(lineText, lineEndOffset);
|
||||
if (result == null) return null;
|
||||
HyperlinkInfo hyperlinkInfo = result.getHyperlinkInfo();
|
||||
@@ -117,8 +114,7 @@ public class ExceptionExFilterFactory implements ExceptionFilterFactory {
|
||||
PsiTryStatement parent = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class, true, PsiClass.class);
|
||||
PsiCodeBlock tryBlock = parent != null? parent.getTryBlock() : null;
|
||||
if (tryBlock == null || !tryBlock.getTextRange().contains(offset)) return null;
|
||||
Trinity<TextRange, TextRange, TextRange> info = worker.getInfo();
|
||||
return info;
|
||||
return worker.getInfo();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+4
-4
@@ -127,9 +127,9 @@ public class VcsContentAnnotationExceptionFilter implements Filter, FilterMixin
|
||||
final Document document = getDocumentForFile(worker);
|
||||
if (document == null) return;
|
||||
|
||||
int startFileOffset = worker.getInfo().getThird().getStartOffset();
|
||||
int startFileOffset = worker.getInfo().fileLineRange.getStartOffset();
|
||||
int idx = lineText.indexOf(':', startFileOffset);
|
||||
int endIdx = idx == -1 ? worker.getInfo().getThird().getEndOffset() : idx;
|
||||
int endIdx = idx == -1 ? worker.getInfo().fileLineRange.getEndOffset() : idx;
|
||||
consumer.consume(new MyAdditionalHighlight(startOffset + lineStartOffset + startFileOffset + 1, startOffset + lineStartOffset + endIdx));
|
||||
|
||||
if (worker.getPsiClass() != null) {
|
||||
@@ -149,8 +149,8 @@ public class VcsContentAnnotationExceptionFilter implements Filter, FilterMixin
|
||||
}
|
||||
}
|
||||
if (methodChanged) {
|
||||
consumer.consume(new MyAdditionalHighlight(startOffset + lineStartOffset + worker.getInfo().getSecond().getStartOffset(),
|
||||
startOffset + lineStartOffset + worker.getInfo().getSecond().getEndOffset()));
|
||||
consumer.consume(new MyAdditionalHighlight(startOffset + lineStartOffset + worker.getInfo().methodNameRange.getStartOffset(),
|
||||
startOffset + lineStartOffset + worker.getInfo().methodNameRange.getEndOffset()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class ThreadDumpParser {
|
||||
private static final Pattern ourThreadStartPattern = Pattern.compile("^\\s*\"(.+)\".+(prio=\\d+ (?:os_prio=[^\\s]+ )?tid=[^\\s]+ nid=[^\\s]+|[Ii][Dd]=\\d+) ([^\\[]+)");
|
||||
private static final Pattern ourForcedThreadStartPattern = Pattern.compile("^\\s*Thread (\\d+): \\(state = (.+)\\)");
|
||||
private static final Pattern ourYourkitThreadStartPattern = Pattern.compile("(?:\\s)*(.+) \\[([A-Z_, ]*)]");
|
||||
private static final Pattern ourYourkitThreadStartPattern2 = Pattern.compile("(.+) State: (.+) CPU usage on sample: .+");
|
||||
private static final Pattern ourThreadStatePattern = Pattern.compile("java\\.lang\\.Thread\\.State: (.+) \\((.+)\\)");
|
||||
@@ -198,7 +199,12 @@ public class ThreadDumpParser {
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
m = ourForcedThreadStartPattern.matcher(line);
|
||||
if (m.matches()) {
|
||||
return new ThreadState(m.group(1), m.group(2));
|
||||
}
|
||||
|
||||
boolean daemon = line.contains(" [DAEMON]");
|
||||
if (daemon) {
|
||||
line = StringUtil.replace(line, " [DAEMON]", "");
|
||||
|
||||
+16
-17
@@ -21,7 +21,6 @@ import com.intellij.execution.filters.FilterMixin;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.Trinity;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
|
||||
@@ -79,29 +78,29 @@ public class ExceptionWorkerTest extends LightCodeInsightFixtureTestCase {
|
||||
}
|
||||
|
||||
public void testAnomalyParenthesisParsing() {
|
||||
String[][] data = new String[][]{
|
||||
{"at youtrack.jetbrains.com.Issue.IDEA_125137()(FooTest.groovy:2)\n", "youtrack.jetbrains.com.Issue", "IDEA_125137()",
|
||||
"FooTest.groovy:2"},
|
||||
{"at youtrack.jetbrains.com.Issue.IDEA_125137()Hmm(FooTest.groovy:2)\n", "youtrack.jetbrains.com.Issue", "IDEA_125137()Hmm",
|
||||
"FooTest.groovy:2"},
|
||||
{"p1.Cl.mee(p1.Cl.java:87) (A MESSAGE) IDEA-133794 (BUG START WITH 1)\n", "p1.Cl", "mee", "p1.Cl.java:87"}
|
||||
};
|
||||
for (String[] datum : data) {
|
||||
assertParsed(datum[0], datum[1], datum[2], datum[3]);
|
||||
}
|
||||
assertParsed("at youtrack.jetbrains.com.Issue.IDEA_125137()(FooTest.groovy:2)\n", "youtrack.jetbrains.com.Issue", "IDEA_125137()", "FooTest.groovy", 2);
|
||||
assertParsed("at youtrack.jetbrains.com.Issue.IDEA_125137()Hmm(FooTest.groovy:2)\n", "youtrack.jetbrains.com.Issue", "IDEA_125137()Hmm", "FooTest.groovy", 2);
|
||||
assertParsed("p1.Cl.mee(p1.Cl.java:87) (A MESSAGE) IDEA-133794 (BUG START WITH 1)\n", "p1.Cl", "mee", "p1.Cl.java", 87);
|
||||
}
|
||||
|
||||
private static void assertParsed(String line, String className, String methodName, String fileLine) {
|
||||
private static void assertParsed(String line, String className, String methodName, String fileName, int lineIndex) {
|
||||
assertTrue(line.endsWith("\n"));
|
||||
Trinity<TextRange, TextRange, TextRange> trinity = ExceptionWorker.parseExceptionLine(line);
|
||||
ExceptionWorker.ParsedLine trinity = ExceptionWorker.parseExceptionLine(line);
|
||||
assertNotNull(trinity);
|
||||
assertEquals(className, trinity.first.subSequence(line));
|
||||
assertEquals(methodName, trinity.second.subSequence(line));
|
||||
assertEquals(fileLine, trinity.third.subSequence(line));
|
||||
assertEquals(className, trinity.classFqnRange.subSequence(line));
|
||||
assertEquals(methodName, trinity.methodNameRange.subSequence(line));
|
||||
assertEquals(fileName, trinity.fileName);
|
||||
assertEquals(lineIndex, trinity.lineNumber);
|
||||
}
|
||||
|
||||
public void testYourKitFormat() {
|
||||
assertParsed("com.intellij.util.concurrency.Semaphore.waitFor(long) Semaphore.java:89\n",
|
||||
"com.intellij.util.concurrency.Semaphore", "waitFor", "Semaphore.java:89");
|
||||
"com.intellij.util.concurrency.Semaphore", "waitFor", "Semaphore.java", 89);
|
||||
}
|
||||
|
||||
public void testForcedJstackFormat() {
|
||||
assertParsed(" - java.lang.ref.ReferenceQueue.remove(long) @bci=151, line=143 (Compiled frame)\n",
|
||||
"java.lang.ref.ReferenceQueue", "remove", null, 143);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,4 +228,44 @@ com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(Runnable) A
|
||||
'AWT-EventQueue-0 2017.3#IC-173.SNAPSHOT IDEA, eap:true, os:Linux 3.13.0-117-generic, java-version:JetBrains s.r.o 1.8.0_152-release-867-b1']
|
||||
}
|
||||
|
||||
void "test jstack -F format"() {
|
||||
String text = '''
|
||||
Attaching to process ID 7370, please wait...
|
||||
Debugger attached successfully.
|
||||
Server compiler detected.
|
||||
JVM version is 25.161-b12
|
||||
Deadlock Detection:
|
||||
|
||||
No deadlocks found.
|
||||
|
||||
Thread 8393: (state = BLOCKED)
|
||||
- sun.misc.Unsafe.park(boolean, long) @bci=0 (Compiled frame; information may be imprecise)
|
||||
- java.util.concurrent.locks.LockSupport.parkNanos(java.lang.Object, long) @bci=63, line=215 (Compiled frame)
|
||||
- java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(java.util.concurrent.SynchronousQueue$TransferStack$SNode, boolean, long) @bci=283, line=460 (Compiled frame)
|
||||
- java.util.concurrent.SynchronousQueue$TransferStack.transfer(java.lang.Object, boolean, long) @bci=175, line=362 (Compiled frame)
|
||||
- java.util.concurrent.SynchronousQueue.poll(long, java.util.concurrent.TimeUnit) @bci=49, line=941 (Compiled frame)
|
||||
- java.util.concurrent.ThreadPoolExecutor.getTask() @bci=247, line=1073 (Compiled frame)
|
||||
- java.util.concurrent.ThreadPoolExecutor.runWorker(java.util.concurrent.ThreadPoolExecutor$Worker) @bci=74, line=1134 (Interpreted frame)
|
||||
- java.util.concurrent.ThreadPoolExecutor$Worker.run() @bci=28, line=624 (Interpreted frame)
|
||||
- java.lang.Thread.run() @bci=34, line=748 (Interpreted frame)
|
||||
|
||||
|
||||
Thread 7399: (state = IN_NATIVE)
|
||||
- sun.awt.X11.XToolkit.$$YJP$$waitForEvents(long) @bci=0 (Compiled frame; information may be imprecise)
|
||||
- sun.awt.X11.XToolkit.waitForEvents(long) @bci=14 (Compiled frame)
|
||||
- sun.awt.X11.XToolkit.run(boolean) @bci=298, line=568 (Interpreted frame)
|
||||
- sun.awt.X11.XToolkit.run() @bci=38, line=532 (Interpreted frame)
|
||||
- java.lang.Thread.run() @bci=34, line=748 (Interpreted frame)
|
||||
|
||||
|
||||
Thread 7381: (state = BLOCKED)
|
||||
'''
|
||||
def threads = ThreadDumpParser.parse(text)
|
||||
assert threads.collect { it.name } == ['8393', '7399', '7381']
|
||||
assert threads[0].stackTrace.contains('ThreadPoolExecutor')
|
||||
assert threads[1].stackTrace.contains('XToolkit')
|
||||
assert threads[2].emptyStackTrace
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.Trinity;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -48,7 +47,7 @@ public class ExceptionWorker {
|
||||
private PsiClass[] myClasses = PsiClass.EMPTY_ARRAY;
|
||||
private PsiFile[] myFiles = PsiFile.EMPTY_ARRAY;
|
||||
private String myMethod;
|
||||
private Trinity<TextRange, TextRange, TextRange> myInfo;
|
||||
private ParsedLine myInfo;
|
||||
private final ExceptionInfoCache myCache;
|
||||
|
||||
public ExceptionWorker(@NotNull ExceptionInfoCache cache) {
|
||||
@@ -63,23 +62,15 @@ public class ExceptionWorker {
|
||||
return null;
|
||||
}
|
||||
|
||||
myMethod = myInfo.getSecond().substring(line);
|
||||
myMethod = myInfo.methodNameRange.substring(line);
|
||||
|
||||
final String fileAndLine = myInfo.third.substring(line).trim();
|
||||
|
||||
final int colonIndex = fileAndLine.lastIndexOf(':');
|
||||
if (colonIndex < 0) return null;
|
||||
|
||||
final int lineNumber = getLineNumber(fileAndLine.substring(colonIndex + 1));
|
||||
if (lineNumber < 0) return null;
|
||||
|
||||
Pair<PsiClass[], PsiFile[]> pair = myCache.resolveClass(myInfo.first.substring(line).trim());
|
||||
Pair<PsiClass[], PsiFile[]> pair = myCache.resolveClass(myInfo.classFqnRange.substring(line).trim());
|
||||
myClasses = pair.first;
|
||||
myFiles = pair.second;
|
||||
if (myFiles.length == 0) {
|
||||
if (myFiles.length == 0 && myInfo.fileName != null) {
|
||||
// try find the file with the required name
|
||||
//todo[nik] it would be better to use FilenameIndex here to honor the scope by it isn't accessible in Open API
|
||||
myFiles = PsiShortNamesCache.getInstance(myProject).getFilesByName(fileAndLine.substring(0, colonIndex).trim());
|
||||
myFiles = PsiShortNamesCache.getInstance(myProject).getFilesByName(myInfo.fileName);
|
||||
}
|
||||
if (myFiles.length == 0) return null;
|
||||
|
||||
@@ -93,8 +84,8 @@ public class ExceptionWorker {
|
||||
|
||||
final int textStartOffset = textEndOffset - line.length();
|
||||
|
||||
final int highlightStartOffset = textStartOffset + myInfo.third.getStartOffset();
|
||||
final int highlightEndOffset = textStartOffset + myInfo.third.getEndOffset();
|
||||
int highlightStartOffset = textStartOffset + myInfo.fileLineRange.getStartOffset();
|
||||
int highlightEndOffset = textStartOffset + myInfo.fileLineRange.getEndOffset();
|
||||
|
||||
ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex();
|
||||
List<VirtualFile> virtualFilesInLibraries = new ArrayList<>();
|
||||
@@ -122,7 +113,7 @@ public class ExceptionWorker {
|
||||
else {
|
||||
virtualFiles = virtualFilesInContent;
|
||||
}
|
||||
HyperlinkInfo linkInfo = HyperlinkInfoFactory.getInstance().createMultipleFilesHyperlinkInfo(virtualFiles, lineNumber - 1, myProject);
|
||||
HyperlinkInfo linkInfo = HyperlinkInfoFactory.getInstance().createMultipleFilesHyperlinkInfo(virtualFiles, myInfo.lineNumber - 1, myProject);
|
||||
Filter.Result result = new Filter.Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes);
|
||||
myResult = result;
|
||||
return result;
|
||||
@@ -158,7 +149,7 @@ public class ExceptionWorker {
|
||||
return ArrayUtil.getFirstElement(myFiles);
|
||||
}
|
||||
|
||||
public Trinity<TextRange, TextRange, TextRange> getInfo() {
|
||||
public ParsedLine getInfo() {
|
||||
return myInfo;
|
||||
}
|
||||
|
||||
@@ -183,41 +174,94 @@ public class ExceptionWorker {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Trinity<TextRange, TextRange, TextRange> parseExceptionLine(final String line) {
|
||||
int startIdx = findAtPrefix(line);
|
||||
|
||||
TextRange yourKitLink = startIdx < 0 ? getYourKitLinkRange(line) : null;
|
||||
int rParenIdx = yourKitLink != null ? yourKitLink.getEndOffset() - 2 : findFirstRParenAfterDigit(line);
|
||||
if (rParenIdx < 0) return null;
|
||||
|
||||
final int lParenIdx = line.lastIndexOf('(', rParenIdx);
|
||||
if (lParenIdx < 0) return null;
|
||||
|
||||
final int dotIdx = line.lastIndexOf('.', lParenIdx);
|
||||
if (dotIdx < 0 || dotIdx < startIdx) return null;
|
||||
int moduleIdx = line.indexOf('/');
|
||||
int classNameIdx = moduleIdx > -1 && moduleIdx < lParenIdx && moduleIdx < dotIdx ? moduleIdx + 1 : startIdx + 1 + (startIdx >= 0 ? AT.length() : 0);
|
||||
|
||||
// class, method, link
|
||||
return Trinity.create(new TextRange(classNameIdx, handleSpaces(line, dotIdx, -1)),
|
||||
new TextRange(handleSpaces(line, dotIdx + 1, 1), handleSpaces(line, lParenIdx, -1)),
|
||||
yourKitLink != null ? yourKitLink : new TextRange(lParenIdx + 1, rParenIdx));
|
||||
public static ParsedLine parseExceptionLine(final String line) {
|
||||
ParsedLine result = parseNormalStackTraceLine(line);
|
||||
if (result == null) result = parseYourKitLine(line);
|
||||
if (result == null) result = parseForcedLine(line);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TextRange getYourKitLinkRange(String line) {
|
||||
private static ParsedLine parseNormalStackTraceLine(String line) {
|
||||
int startIdx = findAtPrefix(line);
|
||||
int rParenIdx = findFirstRParenAfterDigit(line);
|
||||
if (rParenIdx < 0) return null;
|
||||
|
||||
TextRange methodName = findMethodNameCandidateBefore(line, startIdx, rParenIdx);
|
||||
if (methodName == null) return null;
|
||||
|
||||
int lParenIdx = methodName.getEndOffset();
|
||||
int dotIdx = methodName.getStartOffset() - 1;
|
||||
int moduleIdx = line.indexOf('/');
|
||||
int classNameIdx = moduleIdx > -1 && moduleIdx < dotIdx ? moduleIdx + 1 : startIdx + 1 + (startIdx >= 0 ? AT.length() : 0);
|
||||
|
||||
return ParsedLine.createFromFileAndLine(new TextRange(classNameIdx, handleSpaces(line, dotIdx, -1)),
|
||||
trimRange(line, methodName),
|
||||
lParenIdx + 1, rParenIdx, line);
|
||||
}
|
||||
|
||||
private static TextRange trimRange(String line, TextRange range) {
|
||||
int start = handleSpaces(line, range.getStartOffset(), 1);
|
||||
int end = handleSpaces(line, range.getEndOffset(), -1);
|
||||
if (start != range.getStartOffset() || end != range.getEndOffset()) {
|
||||
return TextRange.create(start, end);
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ParsedLine parseYourKitLine(String line) {
|
||||
int lineEnd = line.length() - 1;
|
||||
if (lineEnd > 0 && line.charAt(lineEnd) == '\n') lineEnd--;
|
||||
if (lineEnd > 0 && Character.isDigit(line.charAt(lineEnd))) {
|
||||
int spaceIndex = line.lastIndexOf(' ');
|
||||
int rParenIdx = line.lastIndexOf(')');
|
||||
if (rParenIdx > 0 && spaceIndex == rParenIdx + 1) {
|
||||
return new TextRange(spaceIndex + 1, lineEnd + 1);
|
||||
TextRange methodName = findMethodNameCandidateBefore(line, 0, rParenIdx);
|
||||
if (methodName != null) {
|
||||
return ParsedLine.createFromFileAndLine(new TextRange(0, methodName.getStartOffset() - 1),
|
||||
methodName,
|
||||
spaceIndex + 1, lineEnd + 1,
|
||||
line);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ParsedLine parseForcedLine(String line) {
|
||||
String dash = "- ";
|
||||
if (!line.trim().startsWith(dash)) return null;
|
||||
|
||||
String linePrefix = "line=";
|
||||
int lineNumberStart = line.indexOf(linePrefix);
|
||||
if (lineNumberStart < 0) return null;
|
||||
|
||||
int lineNumberEnd = line.indexOf(' ', lineNumberStart);
|
||||
if (lineNumberEnd < 0) return null;
|
||||
|
||||
TextRange methodName = findMethodNameCandidateBefore(line, 0, lineNumberStart);
|
||||
if (methodName == null) return null;
|
||||
|
||||
int lineNumber = getLineNumber(line.substring(lineNumberStart + linePrefix.length(), lineNumberEnd));
|
||||
if (lineNumber < 0) return null;
|
||||
|
||||
return new ParsedLine(trimRange(line, TextRange.create(line.indexOf(dash) + dash.length(), methodName.getStartOffset() - 1)),
|
||||
methodName,
|
||||
TextRange.create(lineNumberStart, lineNumberEnd), null, lineNumber);
|
||||
}
|
||||
|
||||
private static TextRange findMethodNameCandidateBefore(String line, int start, int end) {
|
||||
int lParenIdx = line.lastIndexOf('(', end);
|
||||
if (lParenIdx < 0) return null;
|
||||
|
||||
int dotIdx = line.lastIndexOf('.', lParenIdx);
|
||||
if (dotIdx < 0 || dotIdx < start) return null;
|
||||
|
||||
return TextRange.create(dotIdx + 1, lParenIdx);
|
||||
}
|
||||
|
||||
private static int handleSpaces(String line, int pos, int delta) {
|
||||
int len = line.length();
|
||||
while (pos >= 0 && pos < len) {
|
||||
@@ -227,4 +271,38 @@ public class ExceptionWorker {
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
public static class ParsedLine {
|
||||
@NotNull public final TextRange classFqnRange;
|
||||
@NotNull public final TextRange methodNameRange;
|
||||
@NotNull public final TextRange fileLineRange;
|
||||
@Nullable public final String fileName;
|
||||
public final int lineNumber;
|
||||
|
||||
ParsedLine(@NotNull TextRange classFqnRange,
|
||||
@NotNull TextRange methodNameRange,
|
||||
@NotNull TextRange fileLineRange, @Nullable String fileName, int lineNumber) {
|
||||
this.classFqnRange = classFqnRange;
|
||||
this.methodNameRange = methodNameRange;
|
||||
this.fileLineRange = fileLineRange;
|
||||
this.fileName = fileName;
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ParsedLine createFromFileAndLine(@NotNull TextRange classFqnRange,
|
||||
@NotNull TextRange methodNameRange,
|
||||
int fileLineStart, int fileLineEnd, String line) {
|
||||
TextRange fileLineRange = TextRange.create(fileLineStart, fileLineEnd);
|
||||
String fileAndLine = fileLineRange.substring(line);
|
||||
|
||||
int colonIndex = fileAndLine.lastIndexOf(':');
|
||||
if (colonIndex < 0) return null;
|
||||
|
||||
int lineNumber = getLineNumber(fileAndLine.substring(colonIndex + 1));
|
||||
if (lineNumber < 0) return null;
|
||||
|
||||
return new ParsedLine(classFqnRange, methodNameRange, fileLineRange, fileAndLine.substring(0, colonIndex).trim(), lineNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user