2017-12-24 03:19:47 +01:00
|
|
|
import {
|
|
|
|
Range
|
|
|
|
} from "./ast";
|
2017-09-28 13:08:25 +02:00
|
|
|
|
2017-12-24 03:19:47 +01:00
|
|
|
import {
|
|
|
|
DiagnosticCode,
|
|
|
|
diagnosticCodeToString
|
|
|
|
} from "./diagnosticMessages.generated";
|
|
|
|
|
|
|
|
import {
|
|
|
|
CharCode,
|
|
|
|
isLineBreak
|
|
|
|
} from "./util/charcode";
|
|
|
|
|
|
|
|
import {
|
|
|
|
sb
|
|
|
|
} from "./util/sb";
|
|
|
|
|
|
|
|
export {
|
|
|
|
DiagnosticCode,
|
|
|
|
diagnosticCodeToString
|
|
|
|
} from "./diagnosticMessages.generated";
|
2017-09-28 13:08:25 +02:00
|
|
|
|
|
|
|
export enum DiagnosticCategory {
|
|
|
|
INFO,
|
|
|
|
WARNING,
|
|
|
|
ERROR
|
|
|
|
}
|
|
|
|
|
|
|
|
export function diagnosticCategoryToString(category: DiagnosticCategory): string {
|
2017-11-17 14:33:51 +01:00
|
|
|
switch (category) {
|
|
|
|
case DiagnosticCategory.INFO: return "INFO";
|
|
|
|
case DiagnosticCategory.WARNING: return "WARNING";
|
|
|
|
case DiagnosticCategory.ERROR: return "ERROR";
|
|
|
|
default: return "";
|
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const colorBlue: string = "\u001b[93m";
|
|
|
|
const colorYellow: string = "\u001b[93m";
|
|
|
|
const colorRed: string = "\u001b[91m";
|
|
|
|
const colorReset: string = "\u001b[0m";
|
|
|
|
|
|
|
|
export function diagnosticCategoryToColor(category: DiagnosticCategory): string {
|
2017-11-17 14:33:51 +01:00
|
|
|
switch (category) {
|
|
|
|
case DiagnosticCategory.INFO: return colorBlue;
|
|
|
|
case DiagnosticCategory.WARNING: return colorYellow;
|
|
|
|
case DiagnosticCategory.ERROR: return colorRed;
|
|
|
|
default: return "";
|
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export class DiagnosticMessage {
|
|
|
|
|
|
|
|
code: i32;
|
|
|
|
category: DiagnosticCategory;
|
|
|
|
message: string;
|
|
|
|
range: Range | null = null;
|
|
|
|
|
|
|
|
constructor(code: i32, category: DiagnosticCategory, message: string) {
|
|
|
|
this.code = code;
|
|
|
|
this.category = category;
|
|
|
|
this.message = message;
|
|
|
|
}
|
|
|
|
|
|
|
|
static create(code: DiagnosticCode, category: DiagnosticCategory, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
|
2017-12-28 04:09:40 +01:00
|
|
|
var message = diagnosticCodeToString(code);
|
2017-09-28 13:08:25 +02:00
|
|
|
if (arg0 != null)
|
|
|
|
message = message.replace("{0}", arg0);
|
|
|
|
if (arg1 != null)
|
|
|
|
message = message.replace("{1}", arg1);
|
|
|
|
return new DiagnosticMessage(code, category, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
static createInfo(code: DiagnosticCode, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
|
|
|
|
return DiagnosticMessage.create(code, DiagnosticCategory.INFO, arg0, arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static createWarning(code: DiagnosticCode, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
|
|
|
|
return DiagnosticMessage.create(code, DiagnosticCategory.WARNING, arg0, arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static createError(code: DiagnosticCode, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
|
|
|
|
return DiagnosticMessage.create(code, DiagnosticCategory.ERROR, arg0, arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
withRange(range: Range): this {
|
|
|
|
this.range = range;
|
|
|
|
return this;
|
|
|
|
}
|
2017-12-14 11:55:35 +01:00
|
|
|
|
|
|
|
toString(): string {
|
|
|
|
if (this.range)
|
2017-12-24 03:19:47 +01:00
|
|
|
return diagnosticCategoryToString(this.category) + " " + this.code.toString(10) + ": \"" + this.message + "\" in " + this.range.source.path + " @ " + this.range.start.toString(10) + "," + this.range.end.toString(10);
|
|
|
|
return diagnosticCategoryToString(this.category) + " " + this.code.toString(10) + ": " + this.message;
|
2017-12-14 11:55:35 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function formatDiagnosticMessage(message: DiagnosticMessage, useColors: bool = false, showContext: bool = false): string {
|
|
|
|
// format context first (uses same string builder)
|
2017-12-28 04:09:40 +01:00
|
|
|
var context = "";
|
2017-09-28 13:08:25 +02:00
|
|
|
if (message.range && showContext)
|
2017-12-24 03:19:47 +01:00
|
|
|
context = formatDiagnosticContext(message.range, useColors);
|
2017-09-28 13:08:25 +02:00
|
|
|
|
|
|
|
// general information
|
|
|
|
sb.length = 0;
|
|
|
|
if (useColors) sb.push(diagnosticCategoryToColor(message.category));
|
|
|
|
sb.push(diagnosticCategoryToString(message.category));
|
|
|
|
if (useColors) sb.push(colorReset);
|
|
|
|
sb.push(" AS");
|
2017-12-11 02:03:15 +01:00
|
|
|
sb.push(message.code.toString(10));
|
2017-09-28 13:08:25 +02:00
|
|
|
sb.push(": ");
|
|
|
|
sb.push(message.message);
|
|
|
|
|
|
|
|
// range information if available
|
|
|
|
if (message.range) {
|
2017-12-28 04:09:40 +01:00
|
|
|
var range = message.range;
|
|
|
|
var text = range.source.text;
|
2017-09-28 13:08:25 +02:00
|
|
|
if (showContext) {
|
|
|
|
sb.push("\n");
|
|
|
|
sb.push(context);
|
|
|
|
}
|
|
|
|
sb.push("\n");
|
|
|
|
sb.push(" in ");
|
|
|
|
sb.push(range.source.path);
|
|
|
|
sb.push("(");
|
2018-01-27 16:23:00 +01:00
|
|
|
sb.push(range.line.toString(10));
|
2017-09-28 13:08:25 +02:00
|
|
|
sb.push(",");
|
2018-01-27 16:23:00 +01:00
|
|
|
sb.push(range.column.toString(10));
|
2017-09-28 13:08:25 +02:00
|
|
|
sb.push(")");
|
|
|
|
}
|
|
|
|
return sb.join("");
|
|
|
|
}
|
|
|
|
|
|
|
|
export function formatDiagnosticContext(range: Range, useColors: bool = false): string {
|
2017-12-28 04:09:40 +01:00
|
|
|
var text = range.source.text;
|
|
|
|
var len = text.length;
|
|
|
|
var start = range.start;
|
|
|
|
var end = range.end;
|
2017-09-28 13:08:25 +02:00
|
|
|
while (start > 0 && !isLineBreak(text.charCodeAt(start - 1)))
|
|
|
|
start--;
|
|
|
|
while (end < len && !isLineBreak(text.charCodeAt(end)))
|
|
|
|
end++;
|
|
|
|
sb.length = 0;
|
|
|
|
sb.push("\n ");
|
|
|
|
sb.push(text.substring(start, end));
|
|
|
|
sb.push("\n ");
|
|
|
|
while (start < range.start) {
|
|
|
|
sb.push(" ");
|
|
|
|
start++;
|
|
|
|
}
|
|
|
|
if (useColors) sb.push(colorRed);
|
|
|
|
if (range.start == range.end) {
|
|
|
|
sb.push("^");
|
|
|
|
} else while (start++ < range.end)
|
|
|
|
sb.push("~");
|
|
|
|
if (useColors) sb.push(colorReset);
|
|
|
|
return sb.join("");
|
|
|
|
}
|
|
|
|
|
|
|
|
export abstract class DiagnosticEmitter {
|
|
|
|
|
|
|
|
diagnostics: DiagnosticMessage[];
|
2017-12-14 11:55:35 +01:00
|
|
|
silentDiagnostics: bool = false;
|
2017-09-28 13:08:25 +02:00
|
|
|
|
|
|
|
constructor(diagnostics: DiagnosticMessage[] | null = null) {
|
|
|
|
this.diagnostics = diagnostics ? <DiagnosticMessage[]>diagnostics : new Array();
|
|
|
|
}
|
|
|
|
|
|
|
|
emitDiagnostic(code: DiagnosticCode, category: DiagnosticCategory, range: Range, arg0: string | null = null, arg1: string | null = null) {
|
2017-12-28 04:09:40 +01:00
|
|
|
var message = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range);
|
2017-09-28 13:08:25 +02:00
|
|
|
this.diagnostics.push(message);
|
2017-12-14 11:55:35 +01:00
|
|
|
if (!this.silentDiagnostics) {
|
|
|
|
console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
|
2018-01-18 01:46:41 +01:00
|
|
|
// console.log(<string>new Error("stack").stack);
|
2017-12-14 11:55:35 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
|
|
|
|
this.emitDiagnostic(code, DiagnosticCategory.ERROR, range, arg0, arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
info(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
|
|
|
|
this.emitDiagnostic(code, DiagnosticCategory.INFO, range, arg0, arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
warning(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
|
|
|
|
this.emitDiagnostic(code, DiagnosticCategory.WARNING, range, arg0, arg1);
|
|
|
|
}
|
|
|
|
}
|