[cli-repl][ide-console] Show runtime errors in IDE console
This commit is contained in:
committed by
Pavel V. Talanov
parent
e23441907c
commit
a17363459a
@@ -134,7 +134,7 @@ public class ReplFromTerminal {
|
|||||||
((FileHistory) consoleReader.getHistory()).flush();
|
((FileHistory) consoleReader.getHistory()).flush();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
System.err.println("failed to flush history: " + e);
|
replWriter.printlnInnerError("failed to flush history: " + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,8 +203,11 @@ public class ReplFromTerminal {
|
|||||||
else if (lineResult.getType() == ReplInterpreter.LineResultType.INCOMPLETE) {
|
else if (lineResult.getType() == ReplInterpreter.LineResultType.INCOMPLETE) {
|
||||||
replWriter.printlnIncomplete();
|
replWriter.printlnIncomplete();
|
||||||
}
|
}
|
||||||
else if (lineResult.getType() == ReplInterpreter.LineResultType.ERROR) {
|
else if (lineResult.getType() == ReplInterpreter.LineResultType.COMPILE_ERROR) {
|
||||||
replWriter.printlnError(lineResult.getErrorText());
|
replWriter.printlnCompileError(lineResult.getErrorText());
|
||||||
|
}
|
||||||
|
else if (lineResult.getType() == ReplInterpreter.LineResultType.RUNTIME_ERROR) {
|
||||||
|
replWriter.printlnRuntimeError(lineResult.getErrorText());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("unknown line result type: " + lineResult);
|
throw new IllegalStateException("unknown line result type: " + lineResult);
|
||||||
|
|||||||
@@ -166,7 +166,8 @@ public class ReplInterpreter {
|
|||||||
|
|
||||||
public enum LineResultType {
|
public enum LineResultType {
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
ERROR,
|
COMPILE_ERROR,
|
||||||
|
RUNTIME_ERROR,
|
||||||
INCOMPLETE,
|
INCOMPLETE,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,11 +210,8 @@ public class ReplInterpreter {
|
|||||||
return errorText;
|
return errorText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LineResult successful(Object value, boolean unit) {
|
@NotNull
|
||||||
return new LineResult(value, unit, null, LineResultType.SUCCESS);
|
private static LineResult error(@NotNull String errorText, @NotNull LineResultType errorType) {
|
||||||
}
|
|
||||||
|
|
||||||
public static LineResult error(@NotNull String errorText) {
|
|
||||||
if (errorText.isEmpty()) {
|
if (errorText.isEmpty()) {
|
||||||
errorText = "<unknown error>";
|
errorText = "<unknown error>";
|
||||||
}
|
}
|
||||||
@@ -221,7 +219,22 @@ public class ReplInterpreter {
|
|||||||
errorText += "\n";
|
errorText += "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LineResult(null, false, errorText, LineResultType.ERROR);
|
return new LineResult(null, false, errorText, errorType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static LineResult successful(Object value, boolean unit) {
|
||||||
|
return new LineResult(value, unit, null, LineResultType.SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static LineResult compileError(@NotNull String errorText) {
|
||||||
|
return error(errorText, LineResultType.COMPILE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static LineResult runtimeError(@NotNull String errorText) {
|
||||||
|
return error(errorText, LineResultType.RUNTIME_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LineResult incomplete() {
|
public static LineResult incomplete() {
|
||||||
@@ -265,7 +278,7 @@ public class ReplInterpreter {
|
|||||||
previousIncompleteLines.clear();
|
previousIncompleteLines.clear();
|
||||||
|
|
||||||
if (syntaxErrorReport.isHasErrors()) {
|
if (syntaxErrorReport.isHasErrors()) {
|
||||||
return LineResult.error(errorHolder.getRenderedDiagnostics());
|
return LineResult.compileError(errorHolder.getRenderedDiagnostics());
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareForTheNextReplLine(topDownAnalysisContext);
|
prepareForTheNextReplLine(topDownAnalysisContext);
|
||||||
@@ -276,7 +289,7 @@ public class ReplInterpreter {
|
|||||||
|
|
||||||
ScriptDescriptor scriptDescriptor = doAnalyze(psiFile, errorHolder);
|
ScriptDescriptor scriptDescriptor = doAnalyze(psiFile, errorHolder);
|
||||||
if (scriptDescriptor == null) {
|
if (scriptDescriptor == null) {
|
||||||
return LineResult.error(errorHolder.getRenderedDiagnostics());
|
return LineResult.compileError(errorHolder.getRenderedDiagnostics());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Pair<ScriptDescriptor, Type>> earlierScripts = Lists.newArrayList();
|
List<Pair<ScriptDescriptor, Type>> earlierScripts = Lists.newArrayList();
|
||||||
@@ -314,7 +327,7 @@ public class ReplInterpreter {
|
|||||||
scriptInstance = scriptInstanceConstructor.newInstance(constructorArgs);
|
scriptInstance = scriptInstanceConstructor.newInstance(constructorArgs);
|
||||||
}
|
}
|
||||||
catch (Throwable e) {
|
catch (Throwable e) {
|
||||||
return LineResult.error(renderStackTrace(e.getCause()));
|
return LineResult.runtimeError(renderStackTrace(e.getCause()));
|
||||||
}
|
}
|
||||||
Field rvField = scriptClass.getDeclaredField("rv");
|
Field rvField = scriptClass.getDeclaredField("rv");
|
||||||
rvField.setAccessible(true);
|
rvField.setAccessible(true);
|
||||||
@@ -351,7 +364,9 @@ public class ReplInterpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.reverse(newTrace);
|
Collections.reverse(newTrace);
|
||||||
cause.setStackTrace(newTrace.toArray(new StackTraceElement[newTrace.size()]));
|
List<StackTraceElement> resultingTrace = newTrace.subList(0, newTrace.size() - 1);
|
||||||
|
|
||||||
|
cause.setStackTrace(resultingTrace.toArray(new StackTraceElement[resultingTrace.size()]));
|
||||||
return Throwables.getStackTraceAsString(cause);
|
return Throwables.getStackTraceAsString(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ public class ReplSystemOutWrapper(private val standardOut: PrintStream, private
|
|||||||
USER_OUTPUT,
|
USER_OUTPUT,
|
||||||
REPL_RESULT,
|
REPL_RESULT,
|
||||||
REPL_INCOMPLETE,
|
REPL_INCOMPLETE,
|
||||||
ERROR
|
COMPILE_ERROR,
|
||||||
|
RUNTIME_ERROR,
|
||||||
|
INNER_ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun print(x: Boolean) = printWithEscaping(x.toString())
|
override fun print(x: Boolean) = printWithEscaping(x.toString())
|
||||||
@@ -54,8 +56,10 @@ public class ReplSystemOutWrapper(private val standardOut: PrintStream, private
|
|||||||
return "${EscapeConstants.XML_PREAMBLE}<output type=\"$escapeType\">${StringUtil.escapeXml(singleLine)}</output>"
|
return "${EscapeConstants.XML_PREAMBLE}<output type=\"$escapeType\">${StringUtil.escapeXml(singleLine)}</output>"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printlnResult(x: Any?) = printlnWithEscaping(x.toString(), EscapeType.REPL_RESULT)
|
|
||||||
fun printlnError(x: String) = printlnWithEscaping(x, EscapeType.ERROR)
|
|
||||||
fun printlnInit(x: String) = printlnWithEscaping(x, EscapeType.INITIAL_PROMPT)
|
fun printlnInit(x: String) = printlnWithEscaping(x, EscapeType.INITIAL_PROMPT)
|
||||||
|
fun printlnResult(x: Any?) = printlnWithEscaping(x.toString(), EscapeType.REPL_RESULT)
|
||||||
fun printlnIncomplete() = printlnWithEscaping("", EscapeType.REPL_INCOMPLETE)
|
fun printlnIncomplete() = printlnWithEscaping("", EscapeType.REPL_INCOMPLETE)
|
||||||
|
fun printlnCompileError(x: String) = printlnWithEscaping(x, EscapeType.COMPILE_ERROR)
|
||||||
|
fun printlnRuntimeError(x: String) = printlnWithEscaping(x, EscapeType.RUNTIME_ERROR)
|
||||||
|
fun printlnInnerError(x: String) = printlnWithEscaping(x, EscapeType.INNER_ERROR)
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,8 @@ public abstract class AbstractReplInterpreterTest : UsefulTestCase() {
|
|||||||
|
|
||||||
val actual = when (lineResult.getType()) {
|
val actual = when (lineResult.getType()) {
|
||||||
ReplInterpreter.LineResultType.SUCCESS -> lineResult.getValue()?.toString() ?: ""
|
ReplInterpreter.LineResultType.SUCCESS -> lineResult.getValue()?.toString() ?: ""
|
||||||
ReplInterpreter.LineResultType.ERROR -> lineResult.getErrorText()
|
ReplInterpreter.LineResultType.RUNTIME_ERROR,
|
||||||
|
ReplInterpreter.LineResultType.COMPILE_ERROR -> lineResult.getErrorText()
|
||||||
ReplInterpreter.LineResultType.INCOMPLETE -> INCOMPLETE_LINE_MESSAGE
|
ReplInterpreter.LineResultType.INCOMPLETE -> INCOMPLETE_LINE_MESSAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.intellij.execution.process.OSProcessHandler
|
|||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import org.jetbrains.kotlin.console.actions.logError
|
||||||
import org.jetbrains.kotlin.console.highlight.KotlinReplOutputHighlighter
|
import org.jetbrains.kotlin.console.highlight.KotlinReplOutputHighlighter
|
||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
@@ -50,32 +51,35 @@ public class KotlinReplOutputHandler(
|
|||||||
when (outputType) {
|
when (outputType) {
|
||||||
"INITIAL_PROMPT" -> super.notifyTextAvailable("$content\n", key)
|
"INITIAL_PROMPT" -> super.notifyTextAvailable("$content\n", key)
|
||||||
"USER_OUTPUT" -> outputHighlighter.printUserOutput(content)
|
"USER_OUTPUT" -> outputHighlighter.printUserOutput(content)
|
||||||
"REPL_RESULT" -> outputHighlighter.printResultWithGutterIcon("$content\n")
|
"REPL_RESULT" -> outputHighlighter.printResultWithGutterIcon(content)
|
||||||
"REPL_INCOMPLETE" -> outputHighlighter.changeIndicatorOnIncomplete()
|
"REPL_INCOMPLETE" -> outputHighlighter.changeIndicatorOnIncomplete()
|
||||||
"ERROR" -> {
|
"COMPILE_ERROR" -> outputHighlighter.highlightCompilerErrors(createCompilerMessages(content))
|
||||||
val compilerMessages = arrayListOf<SeverityDetails>()
|
"RUNTIME_ERROR" -> outputHighlighter.printRuntimeError(content)
|
||||||
|
"INNER_ERROR" -> logError(javaClass, content)
|
||||||
val report = dBuilder.parse(strToSource(content, Charsets.UTF_16))
|
|
||||||
val entries = report.getElementsByTagName("reportEntry")
|
|
||||||
for (i in 0..entries.length - 1) {
|
|
||||||
val reportEntry = entries.item(i) as Element
|
|
||||||
|
|
||||||
val severityLevel = reportEntry.getAttribute("severity").toSeverity()
|
|
||||||
val rangeStart = reportEntry.getAttribute("rangeStart").toInt()
|
|
||||||
val rangeEnd = reportEntry.getAttribute("rangeEnd").toInt()
|
|
||||||
val description = reportEntry.textContent
|
|
||||||
|
|
||||||
compilerMessages.add(SeverityDetails(severityLevel, description, TextRange(rangeStart, rangeEnd)))
|
|
||||||
}
|
|
||||||
|
|
||||||
outputHighlighter.highlightErrors(compilerMessages)
|
|
||||||
}
|
|
||||||
else -> super.notifyTextAvailable("UNEXPECTED TEXT: $content\n", key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
|
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
|
||||||
|
|
||||||
|
private fun createCompilerMessages(runtimeErrorsReport: String): List<SeverityDetails> {
|
||||||
|
val compilerMessages = arrayListOf<SeverityDetails>()
|
||||||
|
|
||||||
|
val report = dBuilder.parse(strToSource(runtimeErrorsReport, Charsets.UTF_16))
|
||||||
|
val entries = report.getElementsByTagName("reportEntry")
|
||||||
|
for (i in 0..entries.length - 1) {
|
||||||
|
val reportEntry = entries.item(i) as Element
|
||||||
|
|
||||||
|
val severityLevel = reportEntry.getAttribute("severity").toSeverity()
|
||||||
|
val rangeStart = reportEntry.getAttribute("rangeStart").toInt()
|
||||||
|
val rangeEnd = reportEntry.getAttribute("rangeEnd").toInt()
|
||||||
|
val description = reportEntry.textContent
|
||||||
|
|
||||||
|
compilerMessages.add(SeverityDetails(severityLevel, description, TextRange(rangeStart, rangeEnd)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return compilerMessages
|
||||||
|
}
|
||||||
|
|
||||||
private fun String.toSeverity() = when (this) {
|
private fun String.toSeverity() = when (this) {
|
||||||
"ERROR" -> Severity.ERROR
|
"ERROR" -> Severity.ERROR
|
||||||
"WARNING" -> Severity.WARNING
|
"WARNING" -> Severity.WARNING
|
||||||
|
|||||||
+8
-1
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.console.highlight
|
|||||||
|
|
||||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
|
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
|
||||||
|
import com.intellij.execution.ui.ConsoleViewContentType
|
||||||
import com.intellij.lang.annotation.HighlightSeverity
|
import com.intellij.lang.annotation.HighlightSeverity
|
||||||
import com.intellij.openapi.command.WriteCommandAction
|
import com.intellij.openapi.command.WriteCommandAction
|
||||||
import com.intellij.openapi.editor.colors.CodeInsightColors
|
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||||
@@ -84,7 +85,7 @@ public class KotlinReplOutputHighlighter(
|
|||||||
runner.changeEditorIndicatorIcon(consoleView.consoleEditor, ReplIcons.INCOMPLETE_INDICATOR)
|
runner.changeEditorIndicatorIcon(consoleView.consoleEditor, ReplIcons.INCOMPLETE_INDICATOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun highlightErrors(compilerMessages: List<SeverityDetails>) = WriteCommandAction.runWriteCommandAction(runner.project) {
|
fun highlightCompilerErrors(compilerMessages: List<SeverityDetails>) = WriteCommandAction.runWriteCommandAction(runner.project) {
|
||||||
resetConsoleEditorIndicator()
|
resetConsoleEditorIndicator()
|
||||||
historyManager.lastCommandType = ReplOutputType.ERROR
|
historyManager.lastCommandType = ReplOutputType.ERROR
|
||||||
|
|
||||||
@@ -112,6 +113,12 @@ public class KotlinReplOutputHighlighter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun printRuntimeError(errorText: String) {
|
||||||
|
resetConsoleEditorIndicator()
|
||||||
|
historyManager.lastCommandType = ReplOutputType.ERROR
|
||||||
|
consoleView.print("\t$errorText", ConsoleViewContentType.ERROR_OUTPUT)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getAttributesForSeverity(start: Int, end: Int, severity: Severity): TextAttributes {
|
private fun getAttributesForSeverity(start: Int, end: Int, severity: Severity): TextAttributes {
|
||||||
val attributes = when (severity) {
|
val attributes = when (severity) {
|
||||||
Severity.ERROR -> getAttributesForSeverity(HighlightInfoType.ERROR, HighlightSeverity.ERROR, CodeInsightColors.ERRORS_ATTRIBUTES, start, end)
|
Severity.ERROR -> getAttributesForSeverity(HighlightInfoType.ERROR, HighlightSeverity.ERROR, CodeInsightColors.ERRORS_ATTRIBUTES, start, end)
|
||||||
|
|||||||
Reference in New Issue
Block a user