diff --git a/java/compiler/impl/src/com/intellij/build/BuildConsoleUtils.java b/java/compiler/impl/src/com/intellij/build/BuildConsoleUtils.java index 4799cf92474d..2271738bbf8b 100644 --- a/java/compiler/impl/src/com/intellij/build/BuildConsoleUtils.java +++ b/java/compiler/impl/src/com/intellij/build/BuildConsoleUtils.java @@ -13,6 +13,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.IJSwingUtilities; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.Nullable; import java.util.Set; import java.util.regex.Matcher; @@ -27,9 +28,13 @@ public class BuildConsoleUtils { private static final String A_CLOSING = ""; private static final Set NEW_LINES = ContainerUtil.set("
", "
", "
", "

", "

", "

", "

", "
"); - public static boolean printFailure(ConsoleView consoleView, Failure failure) { - String text = ObjectUtils.chooseNotNull(failure.getDescription(), failure.getMessage()); - if (text == null && failure.getError() != null) { + public static boolean printDetails(ConsoleView consoleView, Failure failure) { + return printDetails(consoleView, failure, null); + } + + public static boolean printDetails(ConsoleView consoleView, @Nullable Failure failure, @Nullable String details) { + String text = failure == null ? details : ObjectUtils.chooseNotNull(failure.getDescription(), failure.getMessage()); + if (text == null && failure != null && failure.getError() != null) { text = failure.getError().getMessage(); } if (text == null) return false; @@ -52,6 +57,9 @@ public class BuildConsoleUtils { consoleView.printHyperlink(linkText, new HyperlinkInfo() { @Override public void navigate(Project project) { + if(failure == null) { + return; + } Notification notification = failure.getNotification(); if (notification != null && notification.getListener() != null) { notification.getListener().hyperlinkUpdate( diff --git a/java/compiler/impl/src/com/intellij/build/BuildTreeConsoleView.java b/java/compiler/impl/src/com/intellij/build/BuildTreeConsoleView.java index 8da2d43c9708..b45bc1ad2e54 100644 --- a/java/compiler/impl/src/com/intellij/build/BuildTreeConsoleView.java +++ b/java/compiler/impl/src/com/intellij/build/BuildTreeConsoleView.java @@ -33,6 +33,7 @@ import com.intellij.ui.treeStructure.treetable.TreeTable; import com.intellij.ui.treeStructure.treetable.TreeTableTree; import com.intellij.util.EditSourceOnDoubleClickHandler; import com.intellij.util.EditSourceOnEnterKeyHandler; +import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.TransferToEDTQueue; import com.intellij.util.text.DateFormatUtil; @@ -445,6 +446,12 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon public MessageEvent.Kind getKind() { return eventKind; } + + @Override + @Nullable + public String getDetails() { + return null; + } }); } if (messageEvent instanceof FileMessageEvent) { @@ -633,20 +640,39 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon public boolean setNode(@NotNull ExecutionNode node) { EventResult eventResult = node.getResult(); - if (!(eventResult instanceof FailureResult)) return false; - List failures = ((FailureResult)eventResult).getFailures(); - if (failures.isEmpty()) return false; - myConsole.clear(); - boolean hasChanged = false; - for (Iterator iterator = failures.iterator(); iterator.hasNext(); ) { - Failure failure = iterator.next(); - if (!printFailure(failure)) continue; - hasChanged = true; - if (iterator.hasNext()) { - myConsole.print("\n\n", ConsoleViewContentType.NORMAL_OUTPUT); + + if (eventResult instanceof FailureResult) { + myConsole.clear(); + List failures = ((FailureResult)eventResult).getFailures(); + if (failures.isEmpty()) return false; + for (Iterator iterator = failures.iterator(); iterator.hasNext(); ) { + Failure failure = iterator.next(); + String text = ObjectUtils.chooseNotNull(failure.getDescription(), failure.getMessage()); + if (text == null && failure.getError() != null) { + text = failure.getError().getMessage(); + } + if (text == null) continue; + printDetails(failure, text); + hasChanged = true; + if (iterator.hasNext()) { + myConsole.print("\n\n", ConsoleViewContentType.NORMAL_OUTPUT); + } } } + else if (eventResult instanceof MessageEventResult) { + String details = ((MessageEventResult)eventResult).getDetails(); + if (details == null) { + return false; + } + if (details.isEmpty()) { + return false; + } + myConsole.clear(); + printDetails(null, details); + hasChanged = true; + } + if (!hasChanged) return false; myConsole.scrollTo(0); @@ -661,8 +687,8 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon return true; } - private boolean printFailure(Failure failure) { - return BuildConsoleUtils.printFailure(myConsole, failure); + private boolean printDetails(Failure failure, @Nullable String details) { + return BuildConsoleUtils.printDetails(myConsole, failure, details); } public void setNode(@Nullable DefaultMutableTreeNode node) { diff --git a/java/compiler/impl/src/com/intellij/build/events/MessageEventResult.java b/java/compiler/impl/src/com/intellij/build/events/MessageEventResult.java index 60862b8c7ef2..d8263ed6e768 100644 --- a/java/compiler/impl/src/com/intellij/build/events/MessageEventResult.java +++ b/java/compiler/impl/src/com/intellij/build/events/MessageEventResult.java @@ -2,6 +2,7 @@ package com.intellij.build.events; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; /** * @author Vladislav.Soroka @@ -9,4 +10,7 @@ import org.jetbrains.annotations.ApiStatus; @ApiStatus.Experimental public interface MessageEventResult extends EventResult { MessageEvent.Kind getKind(); + + @Nullable + String getDetails(); } diff --git a/java/compiler/impl/src/com/intellij/build/events/impl/FileMessageEventImpl.java b/java/compiler/impl/src/com/intellij/build/events/impl/FileMessageEventImpl.java index 5447d03efdd9..c1c845d7d137 100644 --- a/java/compiler/impl/src/com/intellij/build/events/impl/FileMessageEventImpl.java +++ b/java/compiler/impl/src/com/intellij/build/events/impl/FileMessageEventImpl.java @@ -18,14 +18,17 @@ import java.util.Objects; public class FileMessageEventImpl extends MessageEventImpl implements FileMessageEvent { private final FilePosition myFilePosition; + private final String myDetailedMessage; public FileMessageEventImpl(@NotNull Object parentId, @NotNull Kind kind, @Nullable String group, @NotNull String message, + @Nullable String detailedMessage, @NotNull FilePosition filePosition) { - super(parentId, kind, group, message); + super(parentId, kind, group, message, detailedMessage); myFilePosition = filePosition; + myDetailedMessage = detailedMessage; } @Override @@ -40,6 +43,12 @@ public class FileMessageEventImpl extends MessageEventImpl implements FileMessag public Kind getKind() { return FileMessageEventImpl.this.getKind(); } + + @Override + @Nullable + public String getDetails() { + return myDetailedMessage; + } }; } diff --git a/java/compiler/impl/src/com/intellij/build/events/impl/MessageEventImpl.java b/java/compiler/impl/src/com/intellij/build/events/impl/MessageEventImpl.java index 0111c8769f2e..72dedaf3946c 100644 --- a/java/compiler/impl/src/com/intellij/build/events/impl/MessageEventImpl.java +++ b/java/compiler/impl/src/com/intellij/build/events/impl/MessageEventImpl.java @@ -15,13 +15,15 @@ import java.util.Objects; */ public class MessageEventImpl extends AbstractBuildEvent implements MessageEvent { - private final Kind myKind; - private final String myGroup; + @NotNull private final Kind myKind; + @NotNull private final String myGroup; + @Nullable private final String myDetailedMessage; - public MessageEventImpl(@NotNull Object parentId, @NotNull Kind kind, @Nullable String group, @NotNull String message) { + public MessageEventImpl(@NotNull Object parentId, @NotNull Kind kind, @Nullable String group, @NotNull String message, @Nullable String detailedMessage) { super(new Object(), parentId, System.currentTimeMillis(), message); myKind = kind; myGroup = group == null ? "Other messages" : group; + myDetailedMessage = detailedMessage; } @NotNull @@ -49,6 +51,11 @@ public class MessageEventImpl extends AbstractBuildEvent implements MessageEvent public Kind getKind() { return myKind; } + + @Override + public String getDetails() { + return myDetailedMessage; + } }; } diff --git a/java/compiler/impl/src/com/intellij/build/output/BuildOutputInstantReader.java b/java/compiler/impl/src/com/intellij/build/output/BuildOutputInstantReader.java index cd32043e84ec..5bc7948a6d2b 100644 --- a/java/compiler/impl/src/com/intellij/build/output/BuildOutputInstantReader.java +++ b/java/compiler/impl/src/com/intellij/build/output/BuildOutputInstantReader.java @@ -2,6 +2,7 @@ package com.intellij.build.output; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -16,5 +17,19 @@ public interface BuildOutputInstantReader { void pushBack(); + /*** + * Push back the given number of lines. + * @param numberOfLines + */ + void pushBack(int numberOfLines); + String getCurrentLine(); + + /*** + * Read lines until a given line is found or there are no more lines. The match is done by {@link String#equals(Object)} if endLine is not null. + * @param endLine + * @return All lines read until endLine is found or no more lines exist + */ + @NotNull + String readUntil(@Nullable String endLine); } diff --git a/java/compiler/impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.java b/java/compiler/impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.java index 5c4fca8ee8a6..352d5f4d40c5 100644 --- a/java/compiler/impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.java +++ b/java/compiler/impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.java @@ -4,10 +4,13 @@ package com.intellij.build.output; import com.intellij.build.BuildProgressListener; import com.intellij.build.events.MessageEvent; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.SystemProperties; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.Closeable; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -160,8 +163,35 @@ public class BuildOutputInstantReaderImpl implements Appendable, Closeable, Buil myCurrentIndex--; } + @Override + public void pushBack(int numberOfLines) { + myCurrentIndex -= numberOfLines; + } + @Override public String getCurrentLine() { return myLinesBuffer.size() > myCurrentIndex ? myLinesBuffer.get(myCurrentIndex) : null; } + + @NotNull + @Override + public String readUntil(@Nullable String endString) { + List lineList = new ArrayList<>(); + int readLines = 0; + while (true) { + String currentLine = readLine(); + if (currentLine == null) { + break; + } + readLines++; + if (currentLine.equals(endString)) { + break; + } + else { + lineList.add(currentLine); + } + } + pushBack(readLines); + return StringUtil.join(lineList, SystemProperties.getLineSeparator()); + } } diff --git a/java/compiler/impl/src/com/intellij/build/output/JavacOutputParser.java b/java/compiler/impl/src/com/intellij/build/output/JavacOutputParser.java index 2a954710d727..ef2cbb9c16ce 100644 --- a/java/compiler/impl/src/com/intellij/build/output/JavacOutputParser.java +++ b/java/compiler/impl/src/com/intellij/build/output/JavacOutputParser.java @@ -26,6 +26,9 @@ public class JavacOutputParser implements BuildOutputParser { private static final char COLON = ':'; private static final String WARNING_PREFIX = "warning:"; // default value + private static final String END_DETAIL = "* Try:"; + + @Override public boolean parse(@NotNull String line, @NotNull BuildOutputInstantReader reader, @NotNull Consumer messageConsumer) { int colonIndex1 = line.indexOf(COLON); if (colonIndex1 == 1) { // drive letter @@ -37,17 +40,20 @@ public class JavacOutputParser implements BuildOutputParser { if (part1.equalsIgnoreCase("error") /* jikes */ || part1.equalsIgnoreCase("Caused by")) { // +1 so we don't include the colon String text = line.substring(colonIndex1 + 1).trim(); - messageConsumer.accept(new MessageEventImpl(reader.getBuildId(), MessageEvent.Kind.ERROR, COMPILER_MESSAGES_GROUP, text)); + String detailMessage = getDetailMessage(line, reader); + messageConsumer.accept(new MessageEventImpl(reader.getBuildId(), MessageEvent.Kind.ERROR, COMPILER_MESSAGES_GROUP, text, detailMessage)); return true; } if (part1.equalsIgnoreCase("warning")) { // +1 so we don't include the colon String text = line.substring(colonIndex1 + 1).trim(); - messageConsumer.accept(new MessageEventImpl(reader.getBuildId(), MessageEvent.Kind.WARNING, COMPILER_MESSAGES_GROUP, text)); + String detailMessage = getDetailMessage(line, reader); + messageConsumer.accept(new MessageEventImpl(reader.getBuildId(), MessageEvent.Kind.WARNING, COMPILER_MESSAGES_GROUP, text, detailMessage)); return true; } if (part1.equalsIgnoreCase("javac")) { - messageConsumer.accept(new MessageEventImpl(reader.getBuildId(), MessageEvent.Kind.ERROR, COMPILER_MESSAGES_GROUP, line)); + String detailMessage = getDetailMessage(line, reader); + messageConsumer.accept(new MessageEventImpl(reader.getBuildId(), MessageEvent.Kind.ERROR, COMPILER_MESSAGES_GROUP, line, detailMessage)); return true; } @@ -74,6 +80,7 @@ public class JavacOutputParser implements BuildOutputParser { return false; } + String detailMessage = getDetailMessage(line, reader); List messageList = ContainerUtil.newArrayList(); messageList.add(text); int column; // 0-based. @@ -107,7 +114,7 @@ public class JavacOutputParser implements BuildOutputParser { if (column >= 0) { messageList = convertMessages(messageList); String msgText = StringUtil.join(messageList, SystemProperties.getLineSeparator()); - messageConsumer.accept(new FileMessageEventImpl(reader.getBuildId(), kind, COMPILER_MESSAGES_GROUP, msgText, + messageConsumer.accept(new FileMessageEventImpl(reader.getBuildId(), kind, COMPILER_MESSAGES_GROUP, msgText, detailMessage, new FilePosition(file, lineNumber - 1, column))); return true; } @@ -118,13 +125,19 @@ public class JavacOutputParser implements BuildOutputParser { } if (line.endsWith("java.lang.OutOfMemoryError")) { - messageConsumer.accept(new MessageEventImpl(reader.getBuildId(), MessageEvent.Kind.ERROR, COMPILER_MESSAGES_GROUP, "Out of memory.")); + String detailMessage = reader.readUntil(null); + messageConsumer.accept(new MessageEventImpl(reader.getBuildId(), MessageEvent.Kind.ERROR, COMPILER_MESSAGES_GROUP, "Out of memory.", detailMessage)); return true; } return false; } + @NotNull + private static String getDetailMessage(@NotNull String line, @NotNull BuildOutputInstantReader reader) { + return line + SystemProperties.getLineSeparator() + reader.readUntil(END_DETAIL); + } + private static void addMessage(@NotNull MessageEvent message, @NotNull List messages) { boolean duplicatesPrevious = false; int messageCount = messages.size(); diff --git a/java/compiler/impl/src/com/intellij/build/output/KotlincOutputParser.kt b/java/compiler/impl/src/com/intellij/build/output/KotlincOutputParser.kt index dbc187af1143..e092f04813b0 100644 --- a/java/compiler/impl/src/com/intellij/build/output/KotlincOutputParser.kt +++ b/java/compiler/impl/src/com/intellij/build/output/KotlincOutputParser.kt @@ -6,6 +6,7 @@ import com.intellij.build.events.MessageEvent import com.intellij.build.events.impl.FileMessageEventImpl import com.intellij.build.events.impl.MessageEventImpl import com.intellij.openapi.util.text.StringUtil +import com.intellij.util.SystemProperties import java.io.File import java.lang.IllegalStateException import java.util.function.Consumer @@ -21,6 +22,7 @@ class KotlincOutputParser : BuildOutputParser { companion object { private val COMPILER_MESSAGES_GROUP = "Kotlin compiler" + private val END_DETAIL = "* Try:" } override fun parse(line: String, reader: BuildOutputInstantReader, consumer: Consumer): Boolean { @@ -35,9 +37,11 @@ class KotlincOutputParser : BuildOutputParser { val path = lineWoSeverity.substringBeforeAndTrim(colonIndex2) val file = File(path) + val detail = "$line${SystemProperties.getLineSeparator()}${reader.readUntil(END_DETAIL)}" + val fileExtension = file.extension.toLowerCase() if (!file.isFile || (fileExtension != "kt" && fileExtension != "java")) { - return addMessage(createMessage(reader.buildId, getMessageKind(severity), lineWoSeverity.amendNextLinesIfNeeded(reader)), consumer) + return addMessage(createMessage(reader.buildId, getMessageKind(severity), lineWoSeverity.amendNextLinesIfNeeded(reader), detail), consumer) } val lineWoPath = lineWoSeverity.substringAfterAndTrim(colonIndex2) @@ -54,14 +58,14 @@ class KotlincOutputParser : BuildOutputParser { if (lineNumber != null) { val symbolNumberText = symbolNumber.toInt() return addMessage(createMessageWithLocation( - reader.buildId, getMessageKind(severity), message, path, lineNumber.toInt(), symbolNumberText), consumer) + reader.buildId, getMessageKind(severity), message, path, lineNumber.toInt(), symbolNumberText, detail), consumer) } } - return addMessage(createMessage(reader.buildId, getMessageKind(severity), message), consumer) + return addMessage(createMessage(reader.buildId, getMessageKind(severity), message, detail), consumer) } else { - return addMessage(createMessage(reader.buildId, getMessageKind(severity), lineWoSeverity.amendNextLinesIfNeeded(reader)), consumer) + return addMessage(createMessage(reader.buildId, getMessageKind(severity), lineWoSeverity.amendNextLinesIfNeeded(reader), detail), consumer) } } @@ -142,8 +146,8 @@ class KotlincOutputParser : BuildOutputParser { return true } - private fun createMessage(buildId: Any, messageKind: MessageEvent.Kind, text: String): MessageEvent { - return MessageEventImpl(buildId, messageKind, COMPILER_MESSAGES_GROUP, text.trim()) + private fun createMessage(buildId: Any, messageKind: MessageEvent.Kind, text: String, detail: String): MessageEvent { + return MessageEventImpl(buildId, messageKind, COMPILER_MESSAGES_GROUP, text.trim(), detail) } private fun createMessageWithLocation( @@ -152,9 +156,10 @@ class KotlincOutputParser : BuildOutputParser { text: String, file: String, lineNumber: Int, - columnIndex: Int + columnIndex: Int, + detail: String ): FileMessageEventImpl { - return FileMessageEventImpl(buildId, messageKind, COMPILER_MESSAGES_GROUP, text.trim(), + return FileMessageEventImpl(buildId, messageKind, COMPILER_MESSAGES_GROUP, text.trim(), detail, FilePosition(File(file), lineNumber - 1, columnIndex - 1)) } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java index 158012e43e1a..82c64e606c08 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java @@ -632,7 +632,7 @@ public class ExternalSystemUtil { ExternalSystemProcessHandler processHandler) { if (consoleView instanceof ConsoleView) { for (com.intellij.build.events.Failure failure : failureResult.getFailures()) { - BuildConsoleUtils.printFailure((ConsoleView)consoleView, failure); + BuildConsoleUtils.printDetails((ConsoleView)consoleView, failure); } } else {