Add a detail to MessageEventResult to show more information in BuildView

This CL adds the possibility to have a detailedd message shown when a node
is clicked on BuildView Tree view.

Test: N/A
Bug: 72180932
Change-Id: I69ce91f40a3f90f8601ecd96c9c2863a4e016b0b
This commit is contained in:
Sergio Murguia
2018-01-23 16:29:41 +03:00
committed by Vladislav.Soroka
parent 8cb5818f4c
commit bc54bf10d7
10 changed files with 151 additions and 34 deletions
@@ -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 = "</a>";
private static final Set<String> NEW_LINES = ContainerUtil.set("<br>", "</br>", "<br/>", "<p>", "</p>", "<p/>", "<pre>", "</pre>");
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(
@@ -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<? extends Failure> failures = ((FailureResult)eventResult).getFailures();
if (failures.isEmpty()) return false;
myConsole.clear();
boolean hasChanged = false;
for (Iterator<? extends Failure> 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<? extends Failure> failures = ((FailureResult)eventResult).getFailures();
if (failures.isEmpty()) return false;
for (Iterator<? extends Failure> 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) {
@@ -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();
}
@@ -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;
}
};
}
@@ -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;
}
};
}
@@ -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);
}
@@ -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<String> 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());
}
}
@@ -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<MessageEvent> 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<String> 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<MessageEvent> messages) {
boolean duplicatesPrevious = false;
int messageCount = messages.size();
@@ -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<MessageEvent>): 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))
}
@@ -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 {