From 8c4ed0d57ca9b73ac6ccfb485e2bcc4772dd5f76 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 14 May 2019 15:00:30 -0700 Subject: [PATCH] Push markdown creation out of tsserver --- src/compiler/checker.ts | 47 ++++------ src/compiler/program.ts | 32 ++++++- src/compiler/types.ts | 21 +++-- src/compiler/utilities.ts | 44 ++++----- src/server/protocol.ts | 18 +++- src/server/session.ts | 90 +++++++++++++------ .../reference/api/tsserverlibrary.d.ts | 29 ++++-- tests/baselines/reference/api/typescript.d.ts | 13 ++- 8 files changed, 196 insertions(+), 98 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 86b34c1befe7a..8423a95e522b0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -379,45 +379,34 @@ namespace ts { }, getLocalTypeParametersOfClassOrInterfaceOrTypeAlias, - getPlainDiagnosticRenderingContext: flags => { - return { - typeToString: t => typeToString(t, /*enclosingDeclaration*/ undefined, getTypeFormatFlagsFromRenderFlags(flags)), - symbolToString: s => symbolToString(s), + getDiagnosticRenderingContext: flags => { + let spans: AnnotationSpan[] | undefined; + let offset = 0; + const captureSymbolSpan = (original: string, symbol: Symbol): string => { + (spans || (spans = [])).push({ kind: "symbol", symbol, start: offset + typeWriter.getTextPos(), length: original.length }); + return original; }; - }, - getMarkdownDiagnosticRenderingContext: flags => { - const typeWriter = createTextWriter("", renderIntoMarkdown); + const typeWriter = createTextWriter("", captureSymbolSpan); const typeFormatFlags = getTypeFormatFlagsFromRenderFlags(flags); return { - typeToString: type => { + typeToString: (type, symbolOffset) => { + offset = symbolOffset; typeWriter.clear(); - typeToString(type, /*enclosingDeclaration*/ undefined, typeFormatFlags, typeWriter); - return typeWriter.getText(); + return typeToString(type, /*enclosingDeclaration*/ undefined, typeFormatFlags, typeWriter); }, - symbolToString: symbol => { + symbolToString: (symbol, symbolOffset) => { + offset = symbolOffset; typeWriter.clear(); - symbolToString(symbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined, /*flags*/ undefined, typeWriter); - return typeWriter.getText(); + return symbolToString(symbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined, /*flags*/ undefined, typeWriter); + }, + getPendingAnnotationSpans: () => { + const result = spans; + spans = undefined; + return result; } }; }, }; - const renderIntoMarkdown = (original: string, s: Symbol): string => { - const d = firstOrUndefined(s.declarations); - if (!d) { - return original; - } - const f = getSourceFileOfNode(d); - if (!f) { - return original; - } - const pos = getNameOfDeclaration(d) || d; - const p = getLineAndCharacterOfPosition(f, skipTrivia(f.text, pos.pos)); - return `[${original}](command:editor.action.peekDefinition?${encodeURIComponent(JSON.stringify({ - resource: { $mid: 1, scheme: "file", authority: "", path: startsWith(f.path, "/") ? f.path : `/${f.path}`}, - position: { lineNumber: p.line + 1, column: p.character + 1 } - }))})`; - }; function getTypeFormatFlagsFromRenderFlags(flags: DiagnosticRendererFlags) { // Setting a flag disables the default `TypeFormatFlags.AllowUniqueESSymbolType | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope` flags diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 893f07af04c07..86b77dfde40be 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -502,7 +502,35 @@ namespace ts { return output; } - export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain | undefined, newLine: string, flattenMarkdown?: boolean): string { + /* @internal */ + export function flattenDiagnosticAnnotationSpans(spans: AnnotationSpan[] | DiagnosticMessageChain | undefined, newLine: string): AnnotationSpan[] | undefined { + if (!spans || isArray(spans)) { + return spans; + } + let results: SymbolSpan[] | undefined; + let diagnosticChain: DiagnosticMessageChain | undefined = spans; + let offset = 0; + + let indent = 0; + while (diagnosticChain) { + if (indent) { + offset += newLine.length; + + for (let i = 0; i < indent; i++) { + offset += " ".length; + } + } + if (diagnosticChain.annotations) { + results = concatenate(results, map(diagnosticChain.annotations, d => ({ ...d, start: d.start + offset }))); + } + offset += diagnosticChain.messageText.length; + indent++; + diagnosticChain = diagnosticChain.next; + } + return results; + } + + export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain | undefined, newLine: string): string { if (isString(messageText)) { return messageText; } @@ -519,7 +547,7 @@ namespace ts { result += " "; } } - result += flattenMarkdown && diagnosticChain.markdownText ? diagnosticChain.markdownText : diagnosticChain.messageText; + result += diagnosticChain.messageText; indent++; diagnosticChain = diagnosticChain.next; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1a9c1ec9a55b3..8576f0728916c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3320,8 +3320,7 @@ namespace ts { /* @internal */ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): ReadonlyArray | undefined; - /* @internal */ getPlainDiagnosticRenderingContext(flags: DiagnosticRendererFlags): DiagnosticRenderContext; - /* @internal */ getMarkdownDiagnosticRenderingContext(flags: DiagnosticRendererFlags): DiagnosticRenderContext; + /* @internal */ getDiagnosticRenderingContext(flags: DiagnosticRendererFlags): DiagnosticRenderContext; } @@ -3331,10 +3330,20 @@ namespace ts { UseFullyQualifiedTypes = 1 << 0, } + export type AnnotationSpan = SymbolSpan; + + export interface SymbolSpan { + kind: "symbol"; + symbol: Symbol; + start: number; + length: number; + } + /** @internal */ export interface DiagnosticRenderContext { - typeToString(type: Type): string; - symbolToString(symbol: Symbol): string; + typeToString(type: Type, symbolOffset: number): string; + symbolToString(symbol: Symbol, symbolOffset: number): string; + getPendingAnnotationSpans(): AnnotationSpan[] | undefined; } /* @internal */ @@ -4562,7 +4571,7 @@ namespace ts { */ export interface DiagnosticMessageChain { messageText: string; - markdownText?: string; + annotations?: AnnotationSpan[]; category: DiagnosticCategory; code: number; next?: DiagnosticMessageChain; @@ -4581,7 +4590,7 @@ namespace ts { start: number | undefined; length: number | undefined; messageText: string | DiagnosticMessageChain; - markdownText?: string | DiagnosticMessageChain; + annotations?: AnnotationSpan[]; } export interface DiagnosticWithLocation extends Diagnostic { file: SourceFile; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5b0441a62d2e3..094889093c5c4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -865,8 +865,8 @@ namespace ts { code: messageChain.code, category: messageChain.category, messageText: messageChain.next ? messageChain : messageChain.messageText, - markdownText: messageChain.next ? messageChain : messageChain.markdownText, - relatedInformation + relatedInformation, + ...(!messageChain.next && messageChain.annotations ? { annotations: messageChain.annotations } : {}) }; } @@ -7108,7 +7108,8 @@ namespace ts { getSourceMapSourceConstructor: () => SourceMapSource, }; - function renderForOutput(arg: string | number | Type | Symbol, renderContext: DiagnosticRenderContext | undefined) { + function renderForOutput(arg: string | number | Type | Symbol, renderContext: DiagnosticRenderContext | undefined, offset: number) { + Debug.assertDefined(arg); if (typeof arg === "string" || typeof arg === "number") { return arg; } @@ -7116,13 +7117,18 @@ namespace ts { return Debug.fail("Type or symbol passed into diagnostic rendering pipeline with no renderer provided."); } if (arg.checker) { - return renderContext.typeToString(arg); + return renderContext.typeToString(arg, offset); } - return renderContext.symbolToString(arg); + return renderContext.symbolToString(arg, offset); } export function formatStringFromArgs(text: string, args: ArrayLike, baseIndex = 0, renderContext?: DiagnosticRenderContext): string { - return text.replace(/{(\d+)}/g, (_match, index: string) => "" + Debug.assertDefined(renderForOutput(args[+index + baseIndex], renderContext))); + let offsetAdjustmentFromReplacement = 0; + return text.replace(/{(\d+)}/g, (match: string, index: string, offset: number) => { + const text = "" + renderForOutput(args[+index + baseIndex], renderContext, offset + offsetAdjustmentFromReplacement); + offsetAdjustmentFromReplacement += text.length - match.length; + return text; + }); } export let localizedDiagnosticMessages: MapLike | undefined; @@ -7193,15 +7199,12 @@ namespace ts { export function createRenderedCompilerDiagnostic(checker: TypeChecker, flags: DiagnosticRendererFlags, message: DiagnosticMessage, ...args: (string | number | Type | Symbol | undefined)[]): Diagnostic; export function createRenderedCompilerDiagnostic(checker: TypeChecker, flags: DiagnosticRendererFlags, message: DiagnosticMessage): Diagnostic { let text = getLocaleSpecificMessage(message); - let markdown: string | undefined; + let spans: SymbolSpan[] | undefined; if (arguments.length > 3) { - const unformatted = text; - text = formatStringFromArgs(unformatted, arguments, 3, checker.getPlainDiagnosticRenderingContext(flags)); - const candidateMarkdown = formatStringFromArgs(unformatted, arguments, 3, checker.getMarkdownDiagnosticRenderingContext(flags)); - if (candidateMarkdown !== text) { - markdown = candidateMarkdown; - } + const ctx = checker.getDiagnosticRenderingContext(flags); + text = formatStringFromArgs(text, arguments, 3, ctx); + spans = ctx.getPendingAnnotationSpans(); } return { @@ -7213,7 +7216,7 @@ namespace ts { category: message.category, code: message.code, reportsUnnecessary: message.reportsUnnecessary, - ...(typeof markdown === "string" ? { markdownText: markdown } : {}) + ...(typeof spans === "undefined" ? {} : { annotations: spans }) }; } @@ -7249,15 +7252,12 @@ namespace ts { export function chainRenderedDiagnosticMessages(checker: TypeChecker, flags: DiagnosticRendererFlags, details: DiagnosticMessageChain | undefined, message: DiagnosticMessage, ...args: (string | number | Type | Symbol | undefined)[]): DiagnosticMessageChain; export function chainRenderedDiagnosticMessages(checker: TypeChecker, flags: DiagnosticRendererFlags, details: DiagnosticMessageChain | undefined, message: DiagnosticMessage): DiagnosticMessageChain { let text = getLocaleSpecificMessage(message); - let markdown: string | undefined; + let spans: SymbolSpan[] | undefined; if (arguments.length > 4) { - const unformatted = text; - text = formatStringFromArgs(unformatted, arguments, 4, checker.getPlainDiagnosticRenderingContext(flags)); - const candidateMarkdown = formatStringFromArgs(unformatted, arguments, 4, checker.getMarkdownDiagnosticRenderingContext(flags)); - if (candidateMarkdown !== text) { - markdown = candidateMarkdown; - } + const ctx = checker.getDiagnosticRenderingContext(flags); + text = formatStringFromArgs(text, arguments, 4, ctx); + spans = ctx.getPendingAnnotationSpans(); } return { @@ -7266,7 +7266,7 @@ namespace ts { code: message.code, next: details, - ...(typeof markdown === "string" ? { markdownText: markdown } : {}) + ...(typeof spans === "undefined" ? {} : { annotations: spans }) }; } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 39f0611fbb6f3..b25efe85253e0 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -479,9 +479,19 @@ namespace ts.server.protocol { reportsUnnecessary?: {}; relatedInformation?: DiagnosticRelatedInformation[]; /** - * Markdown containing variation of the message + * List of spans of the message that have associated information */ - markdown?: string; + annotations?: DiagnosticAnnotationSpan[]; + } + + export type DiagnosticAnnotationSpan = DiagnosticSymbolSpan; + + export interface DiagnosticSymbolSpan { + kind: "symbol"; + start: number; + length: number; + file: string; + location: Location; } /** @@ -2390,9 +2400,9 @@ namespace ts.server.protocol { */ source?: string; /** - * Markdown containing variation of the message + * List of spans of the message that have associated information */ - markdown?: string; + annotations?: DiagnosticAnnotationSpan[]; } export interface DiagnosticWithFileName extends Diagnostic { diff --git a/src/server/session.ts b/src/server/session.ts index 974fd959bc4ec..933acf684e27e 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -44,6 +44,10 @@ namespace ts.server { function formatDiag(fileName: NormalizedPath, project: Project, diag: Diagnostic): protocol.Diagnostic { const scriptInfo = project.getScriptInfoForNormalizedPath(fileName)!; // TODO: GH#18217 + const annotations = mapDefined( + flattenDiagnosticAnnotationSpans(typeof diag.messageText !== "string" ? diag.messageText : diag.annotations, "\n"), + convertAnnotationSpanToDiagnosticAnnotationSpan + ); return { start: scriptInfo.positionToLineOffset(diag.start!), end: scriptInfo.positionToLineOffset(diag.start! + diag.length!), // TODO: GH#18217 @@ -53,7 +57,7 @@ namespace ts.server { reportsUnnecessary: diag.reportsUnnecessary, source: diag.source, relatedInformation: map(diag.relatedInformation, formatRelatedInformation), - ...(diag.markdownText ? { markdown: flattenDiagnosticMessageText(diag.markdownText, "\n", /*flattenMarkdown*/ true) } : {}) + ...(length(annotations) ? { annotations } : {}) }; } @@ -883,17 +887,20 @@ namespace ts.server { } private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: ReadonlyArray): protocol.DiagnosticWithLinePosition[] { - return diagnostics.map(d => ({ - message: flattenDiagnosticMessageText(d.messageText, this.host.newLine), - start: d.start!, // TODO: GH#18217 - length: d.length!, // TODO: GH#18217 - category: diagnosticCategoryName(d), - code: d.code, - startLocation: (d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start!)))!, // TODO: GH#18217 - endLocation: (d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start! + d.length!)))!, // TODO: GH#18217 - relatedInformation: map(d.relatedInformation, formatRelatedInformation), - ...(d.markdownText ? { markdown: flattenDiagnosticMessageText(d.markdownText, this.host.newLine, /*flattenMarkdown*/ true) } : {}) - })); + return diagnostics.map(d => { + const annotations = mapDefined(flattenDiagnosticAnnotationSpans(typeof d.messageText !== "string" ? d.messageText : d.annotations, this.host.newLine), convertAnnotationSpanToDiagnosticAnnotationSpan); + return { + message: flattenDiagnosticMessageText(d.messageText, this.host.newLine), + start: d.start!, // TODO: GH#18217 + length: d.length!, // TODO: GH#18217 + category: diagnosticCategoryName(d), + code: d.code, + startLocation: (d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start!)))!, // TODO: GH#18217 + endLocation: (d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start! + d.length!)))!, // TODO: GH#18217 + relatedInformation: map(d.relatedInformation, formatRelatedInformation), + ...(length(annotations) ? { annotations } : {}) + }; + }); } private getCompilerOptionsDiagnostics(args: protocol.CompilerOptionsDiagnosticsRequestArgs) { @@ -911,19 +918,22 @@ namespace ts.server { } private convertToDiagnosticsWithLinePosition(diagnostics: ReadonlyArray, scriptInfo: ScriptInfo | undefined): protocol.DiagnosticWithLinePosition[] { - return diagnostics.map(d => ({ - message: flattenDiagnosticMessageText(d.messageText, this.host.newLine), - start: d.start!, - length: d.length!, - category: diagnosticCategoryName(d), - code: d.code, - source: d.source, - startLocation: (scriptInfo && scriptInfo.positionToLineOffset(d.start!))!, // TODO: GH#18217 - endLocation: (scriptInfo && scriptInfo.positionToLineOffset(d.start! + d.length!))!, - reportsUnnecessary: d.reportsUnnecessary, - relatedInformation: map(d.relatedInformation, formatRelatedInformation), - ...(d.markdownText ? { markdown: flattenDiagnosticMessageText(d.markdownText, this.host.newLine, /*flattenMarkdown*/ true) } : {}) - })); + return diagnostics.map(d => { + const annotations = mapDefined(flattenDiagnosticAnnotationSpans(typeof d.messageText !== "string" ? d.messageText : d.annotations, this.host.newLine), convertAnnotationSpanToDiagnosticAnnotationSpan); + return { + message: flattenDiagnosticMessageText(d.messageText, this.host.newLine), + start: d.start!, + length: d.length!, + category: diagnosticCategoryName(d), + code: d.code, + source: d.source, + startLocation: (scriptInfo && scriptInfo.positionToLineOffset(d.start!))!, // TODO: GH#18217 + endLocation: (scriptInfo && scriptInfo.positionToLineOffset(d.start! + d.length!))!, + reportsUnnecessary: d.reportsUnnecessary, + relatedInformation: map(d.relatedInformation, formatRelatedInformation), + ...(length(annotations) ? { annotations } : {}) + }; + }); } private getDiagnosticsWorker( @@ -2549,6 +2559,36 @@ namespace ts.server { return { line: lc.line + 1, offset: lc.character + 1 }; } + function convertAnnotationSpanToDiagnosticAnnotationSpan(span: AnnotationSpan): protocol.DiagnosticAnnotationSpan | undefined { + switch (span.kind) { + case "symbol": + return convertSymbolSpanIntoDiagnosticSymbolSpan(span); + default: + // TODO: Convert to `Debug.assertNever` once `span` is actually a union + return Debug.fail("Unconverted annotation kind"); + } + } + + function convertSymbolSpanIntoDiagnosticSymbolSpan(span: SymbolSpan): protocol.DiagnosticSymbolSpan | undefined { + if (!span.symbol.declarations || !span.symbol.declarations[0]) { + return undefined; + } + const d = span.symbol.declarations[0]; + const f = getSourceFileOfNode(d); + if (!f) { + return undefined; + } + const pos = getNameOfDeclaration(d) || d; + const p = getLineAndCharacterOfPosition(f, skipTrivia(f.text, pos.pos)); + return { + kind: "symbol", + start: span.start, + length: span.length, + file: f.path, + location: locationFromLineAndCharacter(p) + }; + } + function convertNewFileTextChangeToCodeEdit(textChanges: FileTextChanges): protocol.FileCodeEdits { Debug.assert(textChanges.textChanges.length === 1); const change = first(textChanges.textChanges); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 4cde147defe01..9e1cee19f9618 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2009,6 +2009,13 @@ declare namespace ts { */ runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; } + type AnnotationSpan = SymbolSpan; + interface SymbolSpan { + kind: "symbol"; + symbol: Symbol; + start: number; + length: number; + } enum NodeBuilderFlags { None = 0, NoTruncation = 1, @@ -2448,7 +2455,7 @@ declare namespace ts { */ interface DiagnosticMessageChain { messageText: string; - markdownText?: string; + annotations?: AnnotationSpan[]; category: DiagnosticCategory; code: number; next?: DiagnosticMessageChain; @@ -2466,7 +2473,7 @@ declare namespace ts { start: number | undefined; length: number | undefined; messageText: string | DiagnosticMessageChain; - markdownText?: string | DiagnosticMessageChain; + annotations?: AnnotationSpan[]; } interface DiagnosticWithLocation extends Diagnostic { file: SourceFile; @@ -4271,7 +4278,7 @@ declare namespace ts { function formatDiagnostics(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; - function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain | undefined, newLine: string, flattenMarkdown?: boolean): string; + function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain | undefined, newLine: string): string; function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): ReadonlyArray; /** * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions' @@ -6085,9 +6092,17 @@ declare namespace ts.server.protocol { reportsUnnecessary?: {}; relatedInformation?: DiagnosticRelatedInformation[]; /** - * Markdown containing variation of the message + * List of spans of the message that have associated information */ - markdown?: string; + annotations?: DiagnosticAnnotationSpan[]; + } + type DiagnosticAnnotationSpan = DiagnosticSymbolSpan; + interface DiagnosticSymbolSpan { + kind: "symbol"; + start: number; + length: number; + file: string; + location: Location; } /** * Response message for "projectInfo" request @@ -7586,9 +7601,9 @@ declare namespace ts.server.protocol { */ source?: string; /** - * Markdown containing variation of the message + * List of spans of the message that have associated information */ - markdown?: string; + annotations?: DiagnosticAnnotationSpan[]; } interface DiagnosticWithFileName extends Diagnostic { /** diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 99627afba3ce8..261424c063cef 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2009,6 +2009,13 @@ declare namespace ts { */ runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; } + type AnnotationSpan = SymbolSpan; + interface SymbolSpan { + kind: "symbol"; + symbol: Symbol; + start: number; + length: number; + } enum NodeBuilderFlags { None = 0, NoTruncation = 1, @@ -2448,7 +2455,7 @@ declare namespace ts { */ interface DiagnosticMessageChain { messageText: string; - markdownText?: string; + annotations?: AnnotationSpan[]; category: DiagnosticCategory; code: number; next?: DiagnosticMessageChain; @@ -2466,7 +2473,7 @@ declare namespace ts { start: number | undefined; length: number | undefined; messageText: string | DiagnosticMessageChain; - markdownText?: string | DiagnosticMessageChain; + annotations?: AnnotationSpan[]; } interface DiagnosticWithLocation extends Diagnostic { file: SourceFile; @@ -4271,7 +4278,7 @@ declare namespace ts { function formatDiagnostics(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; - function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain | undefined, newLine: string, flattenMarkdown?: boolean): string; + function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain | undefined, newLine: string): string; function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): ReadonlyArray; /** * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions'