/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.common.messages;
import com.intellij.openapi.util.
SystemInfo;
import com.intellij.util.
LineSeparator;
import kotlin.text.
StringsKt;
import org.fusesource.jansi.
Ansi;
import org.fusesource.jansi.internal.
CLibrary;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.annotations.
Nullable;
import java.util.
EnumSet;
import java.util.
Set;
import static org.jetbrains.kotlin.cli.common.messages.
CompilerMessageSeverity.*;
public abstract class
PlainTextMessageRenderer implements
MessageRenderer {
public static final
String KOTLIN_COLORS_ENABLED_PROPERTY = "kotlin.colors.enabled";
public static final boolean
COLOR_ENABLED;
static {
boolean
colorEnabled = false;
// TODO: investigate why ANSI escape codes on Windows only work in REPL for some reason
if (!
SystemInfo.
isWindows && "true".
equals(
System.
getProperty(
KOTLIN_COLORS_ENABLED_PROPERTY))) {
try {
// AnsiConsole doesn't check isatty() for stderr (see https://github.com/fusesource/jansi/pull/35).
colorEnabled =
CLibrary.
isatty(
CLibrary.
STDERR_FILENO) != 0;
}
catch (
UnsatisfiedLinkError e) {
colorEnabled = false;
}
}
COLOR_ENABLED =
colorEnabled;
}
private static final
String LINE_SEPARATOR =
LineSeparator.
getSystemLineSeparator().
getSeparatorString();
private static final
Set<
CompilerMessageSeverity>
IMPORTANT_MESSAGE_SEVERITIES =
EnumSet.
of(
EXCEPTION,
ERROR,
STRONG_WARNING,
WARNING);
@
Override
public
String renderPreamble() {
return "";
}
@
Override
public
String render(@
NotNull CompilerMessageSeverity severity, @
NotNull String message, @
Nullable CompilerMessageLocation location) {
StringBuilder result = new
StringBuilder();
int
line =
location != null ?
location.
getLine() : -1;
int
column =
location != null ?
location.
getColumn() : -1;
String lineContent =
location != null ?
location.
getLineContent() : null;
String path =
location != null ?
getPath(
location) : null;
if (
path != null) {
result.
append(
path);
result.
append(":");
if (
line > 0) {
result.
append(
line).
append(":");
if (
column > 0) {
result.
append(
column).
append(":");
}
}
result.
append(" ");
}
if (
COLOR_ENABLED) {
Ansi ansi =
Ansi.
ansi()
.
bold()
.
fg(
severityColor(
severity))
.
a(
severity.
getPresentableName())
.
a(": ")
.
reset();
if (
IMPORTANT_MESSAGE_SEVERITIES.
contains(
severity)) {
ansi.
bold();
}
// Only make the first line of the message bold. Otherwise long overload ambiguity errors or exceptions are hard to read
String decapitalized =
decapitalizeIfNeeded(
message);
int
firstNewline =
decapitalized.
indexOf(
LINE_SEPARATOR);
if (
firstNewline < 0) {
result.
append(
ansi.
a(
decapitalized).
reset());
}
else {
result.
append(
ansi.
a(
decapitalized.
substring(0,
firstNewline)).
reset().
a(
decapitalized.
substring(
firstNewline)));
}
}
else {
result.
append(
severity.
getPresentableName());
result.
append(": ");
result.
append(
decapitalizeIfNeeded(
message));
}
if (
lineContent != null && 1 <=
column &&
column <=
lineContent.
length() + 1) {
result.
append(
LINE_SEPARATOR);
result.
append(
lineContent);
result.
append(
LINE_SEPARATOR);
result.
append(
StringsKt.
repeat(" ",
column - 1));
result.
append("^");
}
return
result.
toString();
}
@
NotNull
private static
String decapitalizeIfNeeded(@
NotNull String message) {
// TODO: invent something more clever
// An ad-hoc heuristic to prevent decapitalization of some names
if (
message.
startsWith("Java") ||
message.
startsWith("Kotlin")) {
return
message;
}
// For abbreviations and capitalized text
if (
message.
length() >= 2 &&
Character.
isUpperCase(
message.
charAt(0)) &&
Character.
isUpperCase(
message.
charAt(1))) {
return
message;
}
return
StringsKt.
decapitalize(
message);
}
@
NotNull
private static
Ansi.
Color severityColor(@
NotNull CompilerMessageSeverity severity) {
switch (
severity) {
case
EXCEPTION:
return
Ansi.
Color.
RED;
case
ERROR:
return
Ansi.
Color.
RED;
case
STRONG_WARNING:
return
Ansi.
Color.
YELLOW;
case
WARNING:
return
Ansi.
Color.
YELLOW;
case
INFO:
return
Ansi.
Color.
BLUE;
case
LOGGING:
return
Ansi.
Color.
BLUE;
case
OUTPUT:
return
Ansi.
Color.
BLUE;
default:
throw new
UnsupportedOperationException("Unknown severity: " +
severity);
}
}
@
Nullable
protected abstract
String getPath(@
NotNull CompilerMessageLocation location);
@
Override
public
String renderUsage(@
NotNull String usage) {
return
usage;
}
@
Override
public
String renderConclusion() {
return "";
}
}