Skip to content

Commit 8436dd8

Browse files
committed
Push markdown creation out of tsserver
1 parent 2e10220 commit 8436dd8

File tree

8 files changed

+196
-98
lines changed

8 files changed

+196
-98
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -379,45 +379,34 @@ namespace ts {
379379
},
380380

381381
getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
382-
getPlainDiagnosticRenderingContext: flags => {
383-
return {
384-
typeToString: t => typeToString(t, /*enclosingDeclaration*/ undefined, getTypeFormatFlagsFromRenderFlags(flags)),
385-
symbolToString: s => symbolToString(s),
382+
getDiagnosticRenderingContext: flags => {
383+
let spans: AnnotationSpan[] | undefined;
384+
let offset = 0;
385+
const captureSymbolSpan = (original: string, symbol: Symbol): string => {
386+
(spans || (spans = [])).push({ kind: "symbol", symbol, start: offset + typeWriter.getTextPos(), length: original.length });
387+
return original;
386388
};
387-
},
388-
getMarkdownDiagnosticRenderingContext: flags => {
389-
const typeWriter = createTextWriter("", renderIntoMarkdown);
389+
const typeWriter = createTextWriter("", captureSymbolSpan);
390390
const typeFormatFlags = getTypeFormatFlagsFromRenderFlags(flags);
391391
return {
392-
typeToString: type => {
392+
typeToString: (type, symbolOffset) => {
393+
offset = symbolOffset;
393394
typeWriter.clear();
394-
typeToString(type, /*enclosingDeclaration*/ undefined, typeFormatFlags, typeWriter);
395-
return typeWriter.getText();
395+
return typeToString(type, /*enclosingDeclaration*/ undefined, typeFormatFlags, typeWriter);
396396
},
397-
symbolToString: symbol => {
397+
symbolToString: (symbol, symbolOffset) => {
398+
offset = symbolOffset;
398399
typeWriter.clear();
399-
symbolToString(symbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined, /*flags*/ undefined, typeWriter);
400-
return typeWriter.getText();
400+
return symbolToString(symbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined, /*flags*/ undefined, typeWriter);
401+
},
402+
getPendingAnnotationSpans: () => {
403+
const result = spans;
404+
spans = undefined;
405+
return result;
401406
}
402407
};
403408
},
404409
};
405-
const renderIntoMarkdown = (original: string, s: Symbol): string => {
406-
const d = firstOrUndefined(s.declarations);
407-
if (!d) {
408-
return original;
409-
}
410-
const f = getSourceFileOfNode(d);
411-
if (!f) {
412-
return original;
413-
}
414-
const pos = getNameOfDeclaration(d) || d;
415-
const p = getLineAndCharacterOfPosition(f, skipTrivia(f.text, pos.pos));
416-
return `[${original}](command:editor.action.peekDefinition?${encodeURIComponent(JSON.stringify({
417-
resource: { $mid: 1, scheme: "file", authority: "", path: startsWith(f.path, "/") ? f.path : `/${f.path}`},
418-
position: { lineNumber: p.line + 1, column: p.character + 1 }
419-
}))})`;
420-
};
421410

422411
function getTypeFormatFlagsFromRenderFlags(flags: DiagnosticRendererFlags) {
423412
// Setting a flag disables the default `TypeFormatFlags.AllowUniqueESSymbolType | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope` flags

src/compiler/program.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,35 @@ namespace ts {
502502
return output;
503503
}
504504

505-
export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain | undefined, newLine: string, flattenMarkdown?: boolean): string {
505+
/* @internal */
506+
export function flattenDiagnosticAnnotationSpans(spans: AnnotationSpan[] | DiagnosticMessageChain | undefined, newLine: string): AnnotationSpan[] | undefined {
507+
if (!spans || isArray(spans)) {
508+
return spans;
509+
}
510+
let results: SymbolSpan[] | undefined;
511+
let diagnosticChain: DiagnosticMessageChain | undefined = spans;
512+
let offset = 0;
513+
514+
let indent = 0;
515+
while (diagnosticChain) {
516+
if (indent) {
517+
offset += newLine.length;
518+
519+
for (let i = 0; i < indent; i++) {
520+
offset += " ".length;
521+
}
522+
}
523+
if (diagnosticChain.annotations) {
524+
results = concatenate(results, map(diagnosticChain.annotations, d => ({ ...d, start: d.start + offset })));
525+
}
526+
offset += diagnosticChain.messageText.length;
527+
indent++;
528+
diagnosticChain = diagnosticChain.next;
529+
}
530+
return results;
531+
}
532+
533+
export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain | undefined, newLine: string): string {
506534
if (isString(messageText)) {
507535
return messageText;
508536
}
@@ -519,7 +547,7 @@ namespace ts {
519547
result += " ";
520548
}
521549
}
522-
result += flattenMarkdown && diagnosticChain.markdownText ? diagnosticChain.markdownText : diagnosticChain.messageText;
550+
result += diagnosticChain.messageText;
523551
indent++;
524552
diagnosticChain = diagnosticChain.next;
525553
}

src/compiler/types.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3320,8 +3320,7 @@ namespace ts {
33203320

33213321
/* @internal */ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): ReadonlyArray<TypeParameter> | undefined;
33223322

3323-
/* @internal */ getPlainDiagnosticRenderingContext(flags: DiagnosticRendererFlags): DiagnosticRenderContext;
3324-
/* @internal */ getMarkdownDiagnosticRenderingContext(flags: DiagnosticRendererFlags): DiagnosticRenderContext;
3323+
/* @internal */ getDiagnosticRenderingContext(flags: DiagnosticRendererFlags): DiagnosticRenderContext;
33253324
}
33263325

33273326

@@ -3331,10 +3330,20 @@ namespace ts {
33313330
UseFullyQualifiedTypes = 1 << 0,
33323331
}
33333332

3333+
export type AnnotationSpan = SymbolSpan;
3334+
3335+
export interface SymbolSpan {
3336+
kind: "symbol";
3337+
symbol: Symbol;
3338+
start: number;
3339+
length: number;
3340+
}
3341+
33343342
/** @internal */
33353343
export interface DiagnosticRenderContext {
3336-
typeToString(type: Type): string;
3337-
symbolToString(symbol: Symbol): string;
3344+
typeToString(type: Type, symbolOffset: number): string;
3345+
symbolToString(symbol: Symbol, symbolOffset: number): string;
3346+
getPendingAnnotationSpans(): AnnotationSpan[] | undefined;
33383347
}
33393348

33403349
/* @internal */
@@ -4564,7 +4573,7 @@ namespace ts {
45644573
*/
45654574
export interface DiagnosticMessageChain {
45664575
messageText: string;
4567-
markdownText?: string;
4576+
annotations?: AnnotationSpan[];
45684577
category: DiagnosticCategory;
45694578
code: number;
45704579
next?: DiagnosticMessageChain;
@@ -4583,7 +4592,7 @@ namespace ts {
45834592
start: number | undefined;
45844593
length: number | undefined;
45854594
messageText: string | DiagnosticMessageChain;
4586-
markdownText?: string | DiagnosticMessageChain;
4595+
annotations?: AnnotationSpan[];
45874596
}
45884597
export interface DiagnosticWithLocation extends Diagnostic {
45894598
file: SourceFile;

src/compiler/utilities.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -865,8 +865,8 @@ namespace ts {
865865
code: messageChain.code,
866866
category: messageChain.category,
867867
messageText: messageChain.next ? messageChain : messageChain.messageText,
868-
markdownText: messageChain.next ? messageChain : messageChain.markdownText,
869-
relatedInformation
868+
relatedInformation,
869+
...(!messageChain.next && messageChain.annotations ? { annotations: messageChain.annotations } : {})
870870
};
871871
}
872872

@@ -7108,21 +7108,27 @@ namespace ts {
71087108
getSourceMapSourceConstructor: () => <any>SourceMapSource,
71097109
};
71107110

7111-
function renderForOutput(arg: string | number | Type | Symbol, renderContext: DiagnosticRenderContext | undefined) {
7111+
function renderForOutput(arg: string | number | Type | Symbol, renderContext: DiagnosticRenderContext | undefined, offset: number) {
7112+
Debug.assertDefined(arg);
71127113
if (typeof arg === "string" || typeof arg === "number") {
71137114
return arg;
71147115
}
71157116
if (!renderContext) {
71167117
return Debug.fail("Type or symbol passed into diagnostic rendering pipeline with no renderer provided.");
71177118
}
71187119
if (arg.checker) {
7119-
return renderContext.typeToString(arg);
7120+
return renderContext.typeToString(arg, offset);
71207121
}
7121-
return renderContext.symbolToString(arg);
7122+
return renderContext.symbolToString(arg, offset);
71227123
}
71237124

71247125
export function formatStringFromArgs(text: string, args: ArrayLike<string | number | Type | Symbol>, baseIndex = 0, renderContext?: DiagnosticRenderContext): string {
7125-
return text.replace(/{(\d+)}/g, (_match, index: string) => "" + Debug.assertDefined(renderForOutput(args[+index + baseIndex], renderContext)));
7126+
let offsetAdjustmentFromReplacement = 0;
7127+
return text.replace(/{(\d+)}/g, (match: string, index: string, offset: number) => {
7128+
const text = "" + renderForOutput(args[+index + baseIndex], renderContext, offset + offsetAdjustmentFromReplacement);
7129+
offsetAdjustmentFromReplacement += text.length - match.length;
7130+
return text;
7131+
});
71267132
}
71277133

71287134
export let localizedDiagnosticMessages: MapLike<string> | undefined;
@@ -7193,15 +7199,12 @@ namespace ts {
71937199
export function createRenderedCompilerDiagnostic(checker: TypeChecker, flags: DiagnosticRendererFlags, message: DiagnosticMessage, ...args: (string | number | Type | Symbol | undefined)[]): Diagnostic;
71947200
export function createRenderedCompilerDiagnostic(checker: TypeChecker, flags: DiagnosticRendererFlags, message: DiagnosticMessage): Diagnostic {
71957201
let text = getLocaleSpecificMessage(message);
7196-
let markdown: string | undefined;
7202+
let spans: SymbolSpan[] | undefined;
71977203

71987204
if (arguments.length > 3) {
7199-
const unformatted = text;
7200-
text = formatStringFromArgs(unformatted, arguments, 3, checker.getPlainDiagnosticRenderingContext(flags));
7201-
const candidateMarkdown = formatStringFromArgs(unformatted, arguments, 3, checker.getMarkdownDiagnosticRenderingContext(flags));
7202-
if (candidateMarkdown !== text) {
7203-
markdown = candidateMarkdown;
7204-
}
7205+
const ctx = checker.getDiagnosticRenderingContext(flags);
7206+
text = formatStringFromArgs(text, arguments, 3, ctx);
7207+
spans = ctx.getPendingAnnotationSpans();
72057208
}
72067209

72077210
return {
@@ -7213,7 +7216,7 @@ namespace ts {
72137216
category: message.category,
72147217
code: message.code,
72157218
reportsUnnecessary: message.reportsUnnecessary,
7216-
...(typeof markdown === "string" ? { markdownText: markdown } : {})
7219+
...(typeof spans === "undefined" ? {} : { annotations: spans })
72177220
};
72187221
}
72197222

@@ -7249,15 +7252,12 @@ namespace ts {
72497252
export function chainRenderedDiagnosticMessages(checker: TypeChecker, flags: DiagnosticRendererFlags, details: DiagnosticMessageChain | undefined, message: DiagnosticMessage, ...args: (string | number | Type | Symbol | undefined)[]): DiagnosticMessageChain;
72507253
export function chainRenderedDiagnosticMessages(checker: TypeChecker, flags: DiagnosticRendererFlags, details: DiagnosticMessageChain | undefined, message: DiagnosticMessage): DiagnosticMessageChain {
72517254
let text = getLocaleSpecificMessage(message);
7252-
let markdown: string | undefined;
7255+
let spans: SymbolSpan[] | undefined;
72537256

72547257
if (arguments.length > 4) {
7255-
const unformatted = text;
7256-
text = formatStringFromArgs(unformatted, arguments, 4, checker.getPlainDiagnosticRenderingContext(flags));
7257-
const candidateMarkdown = formatStringFromArgs(unformatted, arguments, 4, checker.getMarkdownDiagnosticRenderingContext(flags));
7258-
if (candidateMarkdown !== text) {
7259-
markdown = candidateMarkdown;
7260-
}
7258+
const ctx = checker.getDiagnosticRenderingContext(flags);
7259+
text = formatStringFromArgs(text, arguments, 4, ctx);
7260+
spans = ctx.getPendingAnnotationSpans();
72617261
}
72627262

72637263
return {
@@ -7266,7 +7266,7 @@ namespace ts {
72667266
code: message.code,
72677267

72687268
next: details,
7269-
...(typeof markdown === "string" ? { markdownText: markdown } : {})
7269+
...(typeof spans === "undefined" ? {} : { annotations: spans })
72707270
};
72717271
}
72727272

src/server/protocol.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -479,9 +479,19 @@ namespace ts.server.protocol {
479479
reportsUnnecessary?: {};
480480
relatedInformation?: DiagnosticRelatedInformation[];
481481
/**
482-
* Markdown containing variation of the message
482+
* List of spans of the message that have associated information
483483
*/
484-
markdown?: string;
484+
annotations?: DiagnosticAnnotationSpan[];
485+
}
486+
487+
export type DiagnosticAnnotationSpan = DiagnosticSymbolSpan;
488+
489+
export interface DiagnosticSymbolSpan {
490+
kind: "symbol";
491+
start: number;
492+
length: number;
493+
file: string;
494+
location: Location;
485495
}
486496

487497
/**
@@ -2390,9 +2400,9 @@ namespace ts.server.protocol {
23902400
*/
23912401
source?: string;
23922402
/**
2393-
* Markdown containing variation of the message
2403+
* List of spans of the message that have associated information
23942404
*/
2395-
markdown?: string;
2405+
annotations?: DiagnosticAnnotationSpan[];
23962406
}
23972407

23982408
export interface DiagnosticWithFileName extends Diagnostic {

0 commit comments

Comments
 (0)