mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #5140 from Microsoft/iFeelPrettyErr
Stylize tsc's error messages with color and context
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/// <reference path="sys.ts"/>
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="diagnosticInformationMap.generated.ts"/>
|
||||
/// <reference path="scanner.ts"/>
|
||||
|
||||
namespace ts {
|
||||
@@ -150,6 +151,12 @@ namespace ts {
|
||||
type: "boolean",
|
||||
description: Diagnostics.Do_not_erase_const_enum_declarations_in_generated_code
|
||||
},
|
||||
{
|
||||
name: "pretty",
|
||||
paramType: Diagnostics.KIND,
|
||||
description: Diagnostics.Stylize_errors_and_messages_using_colors_if_available_experimental,
|
||||
type: "boolean"
|
||||
},
|
||||
{
|
||||
name: "project",
|
||||
shortName: "p",
|
||||
|
||||
@@ -2258,14 +2258,6 @@
|
||||
"code": 6063
|
||||
},
|
||||
|
||||
"Specify JSX code generation: 'preserve' or 'react'": {
|
||||
"category": "Message",
|
||||
"code": 6080
|
||||
},
|
||||
"Argument for '--jsx' must be 'preserve' or 'react'.": {
|
||||
"category": "Message",
|
||||
"code": 6081
|
||||
},
|
||||
"Enables experimental support for ES7 decorators.": {
|
||||
"category": "Message",
|
||||
"code": 6065
|
||||
@@ -2293,8 +2285,8 @@
|
||||
"Suppress excess property checks for object literals.": {
|
||||
"category": "Message",
|
||||
"code": 6072
|
||||
},
|
||||
"Disallow inconsistently-cased references to the same file.": {
|
||||
},
|
||||
"Stylize errors and messages using colors if available. (experimental)": {
|
||||
"category": "Message",
|
||||
"code": 6073
|
||||
},
|
||||
@@ -2314,6 +2306,20 @@
|
||||
"category": "Message",
|
||||
"code": 6077
|
||||
},
|
||||
"Disallow inconsistently-cased references to the same file.": {
|
||||
"category": "Message",
|
||||
"code": 6078
|
||||
},
|
||||
|
||||
"Specify JSX code generation: 'preserve' or 'react'": {
|
||||
"category": "Message",
|
||||
"code": 6080
|
||||
},
|
||||
"Argument for '--jsx' must be 'preserve' or 'react'.": {
|
||||
"category": "Message",
|
||||
"code": 6081
|
||||
},
|
||||
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
"code": 7005
|
||||
|
||||
+2
-9
@@ -198,6 +198,7 @@ namespace ts {
|
||||
const _fs = require("fs");
|
||||
const _path = require("path");
|
||||
const _os = require("os");
|
||||
const _tty = require("tty");
|
||||
|
||||
// average async stat takes about 30 microseconds
|
||||
// set chunk size to do 30 files in < 1 millisecond
|
||||
@@ -375,15 +376,7 @@ namespace ts {
|
||||
newLine: _os.EOL,
|
||||
useCaseSensitiveFileNames: useCaseSensitiveFileNames,
|
||||
write(s: string): void {
|
||||
const buffer = new Buffer(s, "utf8");
|
||||
let offset = 0;
|
||||
let toWrite: number = buffer.length;
|
||||
let written = 0;
|
||||
// 1 is a standard descriptor for stdout
|
||||
while ((written = _fs.writeSync(1, buffer, offset, toWrite)) < toWrite) {
|
||||
offset += written;
|
||||
toWrite -= written;
|
||||
}
|
||||
process.stdout.write(s);
|
||||
},
|
||||
readFile,
|
||||
writeFile,
|
||||
|
||||
+99
-6
@@ -6,6 +6,14 @@ namespace ts {
|
||||
fileWatcher?: FileWatcher;
|
||||
}
|
||||
|
||||
let reportDiagnostic = reportDiagnosticSimply;
|
||||
|
||||
function reportDiagnostics(diagnostics: Diagnostic[], host: CompilerHost): void {
|
||||
for (let diagnostic of diagnostics) {
|
||||
reportDiagnostic(diagnostic, host);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the locale is in the appropriate format,
|
||||
* and if it is, attempts to set the appropriate language.
|
||||
@@ -81,16 +89,16 @@ namespace ts {
|
||||
return <string>diagnostic.messageText;
|
||||
}
|
||||
|
||||
function reportDiagnostic(diagnostic: Diagnostic, host: CompilerHost) {
|
||||
function reportDiagnosticSimply(diagnostic: Diagnostic, host: CompilerHost): void {
|
||||
let output = "";
|
||||
|
||||
if (diagnostic.file) {
|
||||
let loc = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
||||
const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
||||
const relativeFileName = host
|
||||
? convertToRelativePath(diagnostic.file.fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName))
|
||||
: diagnostic.file.fileName;
|
||||
|
||||
output += `${ relativeFileName }(${ loc.line + 1 },${ loc.character + 1 }): `;
|
||||
output += `${ diagnostic.file.fileName }(${ line + 1 },${ character + 1 }): `;
|
||||
}
|
||||
|
||||
let category = DiagnosticCategory[diagnostic.category].toLowerCase();
|
||||
@@ -99,10 +107,91 @@ namespace ts {
|
||||
sys.write(output);
|
||||
}
|
||||
|
||||
function reportDiagnostics(diagnostics: Diagnostic[], host: CompilerHost) {
|
||||
for (let i = 0; i < diagnostics.length; i++) {
|
||||
reportDiagnostic(diagnostics[i], host);
|
||||
|
||||
const redForegroundEscapeSequence = "\u001b[91m";
|
||||
const yellowForegroundEscapeSequence = "\u001b[93m";
|
||||
const blueForegroundEscapeSequence = "\u001b[93m";
|
||||
const gutterStyleSequence = "\u001b[100;30m";
|
||||
const gutterSeparator = " ";
|
||||
const resetEscapeSequence = "\u001b[0m";
|
||||
const elipsis = "...";
|
||||
const categoryFormatMap: Map<string> = {
|
||||
[DiagnosticCategory.Warning]: yellowForegroundEscapeSequence,
|
||||
[DiagnosticCategory.Error]: redForegroundEscapeSequence,
|
||||
[DiagnosticCategory.Message]: blueForegroundEscapeSequence,
|
||||
};
|
||||
|
||||
function formatAndReset(text: string, formatStyle: string) {
|
||||
return formatStyle + text + resetEscapeSequence;
|
||||
}
|
||||
|
||||
function reportDiagnosticWithColorAndContext(diagnostic: Diagnostic, host: CompilerHost): void {
|
||||
let output = "";
|
||||
|
||||
if (diagnostic.file) {
|
||||
let { start, length, file } = diagnostic;
|
||||
let { line: firstLine, character: firstLineChar } = getLineAndCharacterOfPosition(file, start);
|
||||
let { line: lastLine, character: lastLineChar } = getLineAndCharacterOfPosition(file, start + length);
|
||||
const lastLineInFile = getLineAndCharacterOfPosition(file, file.text.length).line;
|
||||
|
||||
let hasMoreThanFiveLines = (lastLine - firstLine) >= 4;
|
||||
let gutterWidth = (lastLine + 1 + "").length;
|
||||
if (hasMoreThanFiveLines) {
|
||||
gutterWidth = Math.max(elipsis.length, gutterWidth);
|
||||
}
|
||||
|
||||
output += sys.newLine;
|
||||
for (let i = firstLine; i <= lastLine; i++) {
|
||||
// If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
|
||||
// so we'll skip ahead to the second-to-last line.
|
||||
if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) {
|
||||
output += formatAndReset(padLeft(elipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + sys.newLine;
|
||||
i = lastLine - 1;
|
||||
}
|
||||
|
||||
let lineStart = getPositionOfLineAndCharacter(file, i, 0);
|
||||
let lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter(file, i + 1, 0) : file.text.length;
|
||||
let lineContent = file.text.slice(lineStart, lineEnd);
|
||||
lineContent = lineContent.replace(/\s+$/g, ""); // trim from end
|
||||
lineContent = lineContent.replace("\t", " "); // convert tabs to single spaces
|
||||
|
||||
// Output the gutter and the actual contents of the line.
|
||||
output += formatAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator;
|
||||
output += lineContent + sys.newLine;
|
||||
|
||||
// Output the gutter and the error span for the line using tildes.
|
||||
output += formatAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator;
|
||||
output += redForegroundEscapeSequence;
|
||||
if (i === firstLine) {
|
||||
// If we're on the last line, then limit it to the last character of the last line.
|
||||
// Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
|
||||
const lastCharForLine = i === lastLine ? lastLineChar : undefined;
|
||||
|
||||
output += lineContent.slice(0, firstLineChar).replace(/\S/g, " ");
|
||||
output += lineContent.slice(firstLineChar, lastCharForLine).replace(/./g, "~");
|
||||
}
|
||||
else if (i === lastLine) {
|
||||
output += lineContent.slice(0, lastLineChar).replace(/./g, "~");
|
||||
}
|
||||
else {
|
||||
// Squiggle the entire line.
|
||||
output += lineContent.replace(/./g, "~");
|
||||
}
|
||||
output += resetEscapeSequence;
|
||||
|
||||
output += sys.newLine;
|
||||
}
|
||||
|
||||
output += sys.newLine;
|
||||
output += `${ file.fileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `;
|
||||
}
|
||||
|
||||
const categoryColor = categoryFormatMap[diagnostic.category];
|
||||
const category = DiagnosticCategory[diagnostic.category].toLowerCase();
|
||||
output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`;
|
||||
output += sys.newLine + sys.newLine;
|
||||
|
||||
sys.write(output);
|
||||
}
|
||||
|
||||
function reportWatchDiagnostic(diagnostic: Diagnostic) {
|
||||
@@ -288,6 +377,10 @@ namespace ts {
|
||||
compilerHost.fileExists = cachedFileExists;
|
||||
}
|
||||
|
||||
if (compilerOptions.pretty) {
|
||||
reportDiagnostic = reportDiagnosticWithColorAndContext;
|
||||
}
|
||||
|
||||
// reset the cache of existing files
|
||||
cachedExistingFiles = {};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"core.ts",
|
||||
"sys.ts",
|
||||
"types.ts",
|
||||
"diagnosticInformationMap.generated.ts",
|
||||
"scanner.ts",
|
||||
"parser.ts",
|
||||
"utilities.ts",
|
||||
@@ -19,7 +20,6 @@
|
||||
"emitter.ts",
|
||||
"program.ts",
|
||||
"commandLineParser.ts",
|
||||
"tsc.ts",
|
||||
"diagnosticInformationMap.generated.ts"
|
||||
"tsc.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2087,6 +2087,7 @@ namespace ts {
|
||||
outFile?: string;
|
||||
outDir?: string;
|
||||
preserveConstEnums?: boolean;
|
||||
/* @internal */ pretty?: DiagnosticStyle;
|
||||
project?: string;
|
||||
removeComments?: boolean;
|
||||
rootDir?: string;
|
||||
@@ -2156,6 +2157,12 @@ namespace ts {
|
||||
JSX,
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum DiagnosticStyle {
|
||||
Simple,
|
||||
Pretty,
|
||||
}
|
||||
|
||||
export interface ParsedCommandLine {
|
||||
options: CompilerOptions;
|
||||
fileNames: string[];
|
||||
|
||||
Reference in New Issue
Block a user