From cd3afe4f0c261f65404c230f22ab555a761cb1b4 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 23 Jan 2023 19:31:30 -0800 Subject: [PATCH 01/16] Reuse Printers in symbolToStringWorker --- src/compiler/checker.ts | 9 ++++++++- src/compiler/core.ts | 30 ++++++++++++++++++++++++++++++ src/compiler/emitter.ts | 3 ++- src/compiler/types.ts | 1 + 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9f77641faf5cc..020c509a96526 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -871,6 +871,7 @@ import { pathIsRelative, PatternAmbientModule, PlusToken, + pooled, PostfixUnaryExpression, PrefixUnaryExpression, PrivateIdentifier, @@ -2173,6 +2174,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { [".json", ".json"], ]; + const symbolToStringWorkerSourceFilePrinter = pooled(() => createPrinter({ removeComments: true, neverAsciiEscape: true }), (printer) => printer.reset()); + const symbolToStringWorkerNonSourceFilePrinter = pooled(() => createPrinter({ removeComments: true }), (printer) => printer.reset()); + initializeTypeChecker(); return checker; @@ -6123,9 +6127,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function symbolToStringWorker(writer: EmitTextWriter) { const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags)!; // TODO: GH#18217 // add neverAsciiEscape for GH#39027 - const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile ? createPrinter({ removeComments: true, neverAsciiEscape: true }) : createPrinter({ removeComments: true }); + const [printer, disposePrinter] = enclosingDeclaration?.kind === SyntaxKind.SourceFile + ? symbolToStringWorkerSourceFilePrinter() + : symbolToStringWorkerNonSourceFilePrinter(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer); + disposePrinter(); return writer; } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 2dc7f533c766a..b308c8e2c3ffa 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2917,3 +2917,33 @@ export function isNodeLikeSystem(): boolean { && !process.browser && typeof module === "object"; } + +/** @internal */ +export function pooled(fn: () => T, reset: (value: T) => void): () => [value: T, dispose: () => void] { + // TODO(jakebailey): this is supposed to be something like Go's sync.Pool, + // but I don't know of a way to "eventually" return the pool's contents to GC. + // For now, this just holds onto the objects even when they may not have been + // in use for a long time. + const pool: T[] = []; + + return () => { + let value: T; + if (pool.length > 1) { + const end = pool.length - 1; + value = pool[end]; + pool.length = end; + } + else { + value = fn(); + } + + const dispose = () => { + if (pool.length < 8) { + reset(value); + pool.push(value); + } + }; + + return [value, dispose]; + }; +} diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 170ad79e7d587..1266772ba801d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1428,7 +1428,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri writeList, writeFile, writeBundle, - bundleFileInfo + bundleFileInfo, + reset, }; function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0a43afe978ea4..758fa517c0b40 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -9165,6 +9165,7 @@ export interface Printer { /** @internal */ writeFile(sourceFile: SourceFile, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined): void; /** @internal */ writeBundle(bundle: Bundle, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined): void; /** @deprecated @internal */ bundleFileInfo?: BundleFileInfo; + /** @internal */ reset(): void; } /** @deprecated @internal */ From db730c9c6ec5e245eba809411694dee9b2dc0f5d Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 23 Jan 2023 20:11:08 -0800 Subject: [PATCH 02/16] Simplify greatly --- src/compiler/checker.ts | 8 +++----- src/compiler/core.ts | 30 ------------------------------ 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 020c509a96526..c69ac537836b6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -871,7 +871,6 @@ import { pathIsRelative, PatternAmbientModule, PlusToken, - pooled, PostfixUnaryExpression, PrefixUnaryExpression, PrivateIdentifier, @@ -2174,8 +2173,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { [".json", ".json"], ]; - const symbolToStringWorkerSourceFilePrinter = pooled(() => createPrinter({ removeComments: true, neverAsciiEscape: true }), (printer) => printer.reset()); - const symbolToStringWorkerNonSourceFilePrinter = pooled(() => createPrinter({ removeComments: true }), (printer) => printer.reset()); + const symbolToStringWorkerSourceFilePrinter = memoize(() => createPrinter({ removeComments: true, neverAsciiEscape: true })); + const symbolToStringWorkerNonSourceFilePrinter = memoize(() => createPrinter({ removeComments: true })); initializeTypeChecker(); @@ -6127,12 +6126,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function symbolToStringWorker(writer: EmitTextWriter) { const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags)!; // TODO: GH#18217 // add neverAsciiEscape for GH#39027 - const [printer, disposePrinter] = enclosingDeclaration?.kind === SyntaxKind.SourceFile + const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile ? symbolToStringWorkerSourceFilePrinter() : symbolToStringWorkerNonSourceFilePrinter(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer); - disposePrinter(); return writer; } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b308c8e2c3ffa..2dc7f533c766a 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2917,33 +2917,3 @@ export function isNodeLikeSystem(): boolean { && !process.browser && typeof module === "object"; } - -/** @internal */ -export function pooled(fn: () => T, reset: (value: T) => void): () => [value: T, dispose: () => void] { - // TODO(jakebailey): this is supposed to be something like Go's sync.Pool, - // but I don't know of a way to "eventually" return the pool's contents to GC. - // For now, this just holds onto the objects even when they may not have been - // in use for a long time. - const pool: T[] = []; - - return () => { - let value: T; - if (pool.length > 1) { - const end = pool.length - 1; - value = pool[end]; - pool.length = end; - } - else { - value = fn(); - } - - const dispose = () => { - if (pool.length < 8) { - reset(value); - pool.push(value); - } - }; - - return [value, dispose]; - }; -} From 93039027d3268c844fa8cd18b7566576ff035de0 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 23 Jan 2023 20:11:44 -0800 Subject: [PATCH 03/16] Remove reset in API --- src/compiler/emitter.ts | 1 - src/compiler/types.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 1266772ba801d..5ed6de9b5dbc2 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1429,7 +1429,6 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri writeFile, writeBundle, bundleFileInfo, - reset, }; function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 758fa517c0b40..0a43afe978ea4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -9165,7 +9165,6 @@ export interface Printer { /** @internal */ writeFile(sourceFile: SourceFile, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined): void; /** @internal */ writeBundle(bundle: Bundle, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined): void; /** @deprecated @internal */ bundleFileInfo?: BundleFileInfo; - /** @internal */ reset(): void; } /** @deprecated @internal */ From 8610e66631d59a38677b8d940010c7815f8e3c56 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 23 Jan 2023 20:13:14 -0800 Subject: [PATCH 04/16] Minimize diff --- src/compiler/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 5ed6de9b5dbc2..170ad79e7d587 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1428,7 +1428,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri writeList, writeFile, writeBundle, - bundleFileInfo, + bundleFileInfo }; function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string { From 6d99c8f7c99b0231092c1a849bee30d556db4d18 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 23 Jan 2023 20:36:59 -0800 Subject: [PATCH 05/16] Reuse a lot more printers --- src/compiler/checker.ts | 19 +++++++++---------- src/compiler/emitter.ts | 12 ++++++++++++ src/services/callHierarchy.ts | 4 ++-- src/services/inlayHints.ts | 6 ++---- src/services/signatureHelp.ts | 8 ++++---- src/services/utilities.ts | 4 ++-- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c69ac537836b6..985893023a30b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -111,7 +111,10 @@ import { createGetCanonicalFileName, createGetSymbolWalker, createModeAwareCacheKey, - createPrinter, + createPrinterWithDefaults, + createPrinterWithRemoveComments, + createPrinterWithRemoveCommentsNeverAsciiEscape, + createPrinterWithRemoveCommentsOmitTrailingSemicolon, createPropertyNameNodeForIdentifierOrLiteral, createSymbolTable, createTextWriter, @@ -2173,9 +2176,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { [".json", ".json"], ]; - const symbolToStringWorkerSourceFilePrinter = memoize(() => createPrinter({ removeComments: true, neverAsciiEscape: true })); - const symbolToStringWorkerNonSourceFilePrinter = memoize(() => createPrinter({ removeComments: true })); - initializeTypeChecker(); return checker; @@ -6127,8 +6127,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags)!; // TODO: GH#18217 // add neverAsciiEscape for GH#39027 const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile - ? symbolToStringWorkerSourceFilePrinter() - : symbolToStringWorkerNonSourceFilePrinter(); + ? createPrinterWithRemoveCommentsNeverAsciiEscape() + : createPrinterWithRemoveComments(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer); return writer; @@ -6147,7 +6147,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { sigOutput = kind === SignatureKind.Construct ? SyntaxKind.ConstructSignature : SyntaxKind.CallSignature; } const sig = nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); - const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true }); + const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonDeferringWriter(writer)); // TODO: GH#18217 return writer; @@ -6160,8 +6160,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (typeNode === undefined) return Debug.fail("should always get typenode"); // The unresolved type gets a synthesized comment on `any` to hint to users that it's not a plain `any`. // Otherwise, we always strip comments out. - const options = { removeComments: type !== unresolvedType }; - const printer = createPrinter(options); + const printer = type !== unresolvedType ? createPrinterWithRemoveComments() : createPrinterWithDefaults(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer); const result = writer.getText(); @@ -9641,7 +9640,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? factory.createIdentifier(typePredicate.parameterName) : factory.createThisTypeNode(), typePredicate.type && nodeBuilder.typeToTypeNode(typePredicate.type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName)! // TODO: GH#18217 ); - const printer = createPrinter({ removeComments: true }); + const printer = createPrinterWithRemoveComments(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, predicate, /*sourceFile*/ sourceFile, writer); return writer; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 170ad79e7d587..e81f2b090a8ae 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1343,6 +1343,18 @@ const enum PipelinePhase { Emit, } +/** @internal */ +export const createPrinterWithDefaults = memoize(() => createPrinter({})); + +/** @internal */ +export const createPrinterWithRemoveComments = memoize(() => createPrinter({ removeComments: true })); + +/** @internal */ +export const createPrinterWithRemoveCommentsNeverAsciiEscape = memoize(() => createPrinter({ removeComments: true, neverAsciiEscape: true })); + +/** @internal */ +export const createPrinterWithRemoveCommentsOmitTrailingSemicolon = memoize(() => createPrinter({ removeComments: true, omitTrailingSemicolon: true })); + export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer { const { hasGlobalName, diff --git a/src/services/callHierarchy.ts b/src/services/callHierarchy.ts index fc4280802ee6d..a2d32e6da717f 100644 --- a/src/services/callHierarchy.ts +++ b/src/services/callHierarchy.ts @@ -14,7 +14,7 @@ import { ClassLikeDeclaration, ClassStaticBlockDeclaration, compareStringsCaseSensitive, - createPrinter, + createPrinterWithRemoveCommentsOmitTrailingSemicolon, createTextRangeFromNode, createTextSpanFromBounds, createTextSpanFromRange, @@ -245,7 +245,7 @@ function getCallHierarchyItemName(program: Program, node: CallHierarchyDeclarati } if (text === undefined) { // get the text from printing the node on a single line without comments... - const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true }); + const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); text = usingSingleLineStringWriter(writer => printer.writeNode(EmitHint.Unspecified, node, node.getSourceFile(), writer)); } return { text, pos: declName.getStart(), end: declName.getEnd() }; diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index 5544aaa920c02..49b113e927554 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -2,7 +2,7 @@ import { __String, ArrowFunction, CallExpression, - createPrinter, + createPrinterWithRemoveComments, Debug, EmitHint, EnumMember, @@ -53,7 +53,6 @@ import { NodeBuilderFlags, ParameterDeclaration, PrefixUnaryExpression, - PrinterOptions, PropertyDeclaration, Signature, skipParentheses, @@ -383,8 +382,7 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { function printTypeInSingleLine(type: Type) { const flags = NodeBuilderFlags.IgnoreErrors | TypeFormatFlags.AllowUniqueESSymbolType | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope; - const options: PrinterOptions = { removeComments: true }; - const printer = createPrinter(options); + const printer = createPrinterWithRemoveComments(); return usingSingleLineStringWriter(writer => { const typeNode = checker.typeToTypeNode(type, /*enclosingDeclaration*/ undefined, flags); diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index f662021481c4c..c8923140c4935 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -7,7 +7,7 @@ import { CheckFlags, contains, countWhere, - createPrinter, + createPrinterWithRemoveComments, createTextSpan, createTextSpanFromBounds, createTextSpanFromNode, @@ -659,7 +659,7 @@ function createTypeHelpItems( function getTypeHelpItem(symbol: Symbol, typeParameters: readonly TypeParameter[], checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItem { const typeSymbolDisplay = symbolToDisplayParts(checker, symbol); - const printer = createPrinter({ removeComments: true }); + const printer = createPrinterWithRemoveComments(); const parameters = typeParameters.map(t => createSignatureHelpParameterForTypeParameter(t, checker, enclosingDeclaration, sourceFile, printer)); const documentation = symbol.getDocumentationComment(checker); @@ -699,7 +699,7 @@ interface SignatureHelpItemInfo { readonly isVariadic: boolean; readonly paramet function itemInfoForTypeParameters(candidateSignature: Signature, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItemInfo[] { const typeParameters = (candidateSignature.target || candidateSignature).typeParameters; - const printer = createPrinter({ removeComments: true }); + const printer = createPrinterWithRemoveComments(); const parameters = (typeParameters || emptyArray).map(t => createSignatureHelpParameterForTypeParameter(t, checker, enclosingDeclaration, sourceFile, printer)); const thisParameter = candidateSignature.thisParameter ? [checker.symbolToParameterDeclaration(candidateSignature.thisParameter, enclosingDeclaration, signatureHelpNodeBuilderFlags)!] : []; @@ -713,7 +713,7 @@ function itemInfoForTypeParameters(candidateSignature: Signature, checker: TypeC } function itemInfoForParameters(candidateSignature: Signature, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItemInfo[] { - const printer = createPrinter({ removeComments: true }); + const printer = createPrinterWithRemoveComments(); const typeParameterParts = mapToDisplayParts(writer => { if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) { const args = factory.createNodeArray(candidateSignature.typeParameters.map(p => checker.typeParameterToDeclaration(p, enclosingDeclaration, signatureHelpNodeBuilderFlags)!)); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index e3de3b3014744..9935d419b2c4b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -30,7 +30,7 @@ import { ConditionalExpression, contains, ContextFlags, - createPrinter, + createPrinterWithRemoveCommentsOmitTrailingSemicolon, createRange, createScanner, createTextSpan, @@ -2984,7 +2984,7 @@ export function signatureToDisplayParts(typechecker: TypeChecker, signature: Sig export function nodeToDisplayParts(node: Node, enclosingDeclaration: Node): SymbolDisplayPart[] { const file = enclosingDeclaration.getSourceFile(); return mapToDisplayParts(writer => { - const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true }); + const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); printer.writeNode(EmitHint.Unspecified, node, file, writer); }); } From 4102e953faf39bcf98ee0d59427db5349412787b Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:40:11 -0800 Subject: [PATCH 06/16] Reuse new createPrinter functions in symbolDisplay --- src/services/symbolDisplay.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index bda7ba595090c..be2e83a48bb2f 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -5,7 +5,7 @@ import { CallExpression, CheckFlags, contains, - createPrinter, + createPrinterWithRemoveComments, Debug, displayPart, EmitHint, @@ -75,7 +75,6 @@ import { NodeBuilderFlags, ObjectFlags, operatorPart, - Printer, PropertyAccessExpression, PropertyDeclaration, punctuationPart, @@ -259,7 +258,6 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ let hasAddedSymbolInfo = false; const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isInExpressionContext(location) || isThisInTypeQuery(location); let type: Type | undefined; - let printer: Printer; let documentationFromAlias: SymbolDisplayPart[] | undefined; let tagsFromAlias: JSDocTagInfo[] | undefined; let hasMultipleSignatures = false; @@ -730,10 +728,7 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ return { displayParts, documentation, symbolKind, tags: tags.length === 0 ? undefined : tags }; function getPrinter() { - if (!printer) { - printer = createPrinter({ removeComments: true }); - } - return printer; + return createPrinterWithRemoveComments(); } function prefixNextMeaning() { From 28dfb919a032fdea9ac9b0d41711369b1628d4bc Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 24 Jan 2023 13:04:17 -0800 Subject: [PATCH 07/16] Use a cache with a key on subset of options --- src/compiler/checker.ts | 16 +++++++--------- src/compiler/emitter.ts | 24 ++++++++++++++++++------ src/services/callHierarchy.ts | 4 ++-- src/services/completions.ts | 3 ++- src/services/inlayHints.ts | 4 ++-- src/services/signatureHelp.ts | 8 ++++---- src/services/symbolDisplay.ts | 4 ++-- src/services/utilities.ts | 4 ++-- 8 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 985893023a30b..32cf2fa054ef1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -111,10 +111,7 @@ import { createGetCanonicalFileName, createGetSymbolWalker, createModeAwareCacheKey, - createPrinterWithDefaults, - createPrinterWithRemoveComments, - createPrinterWithRemoveCommentsNeverAsciiEscape, - createPrinterWithRemoveCommentsOmitTrailingSemicolon, + createOrReusePrinter, createPropertyNameNodeForIdentifierOrLiteral, createSymbolTable, createTextWriter, @@ -6127,8 +6124,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags)!; // TODO: GH#18217 // add neverAsciiEscape for GH#39027 const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile - ? createPrinterWithRemoveCommentsNeverAsciiEscape() - : createPrinterWithRemoveComments(); + ? createOrReusePrinter({ removeComments: true, neverAsciiEscape: true }) + : createOrReusePrinter({ removeComments: true }); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer); return writer; @@ -6147,7 +6144,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { sigOutput = kind === SignatureKind.Construct ? SyntaxKind.ConstructSignature : SyntaxKind.CallSignature; } const sig = nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); - const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); + const printer = createOrReusePrinter({ removeComments: true, omitTrailingSemicolon: true }); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonDeferringWriter(writer)); // TODO: GH#18217 return writer; @@ -6160,7 +6157,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (typeNode === undefined) return Debug.fail("should always get typenode"); // The unresolved type gets a synthesized comment on `any` to hint to users that it's not a plain `any`. // Otherwise, we always strip comments out. - const printer = type !== unresolvedType ? createPrinterWithRemoveComments() : createPrinterWithDefaults(); + const options = { removeComments: type !== unresolvedType }; + const printer = createOrReusePrinter(options); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer); const result = writer.getText(); @@ -9640,7 +9638,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? factory.createIdentifier(typePredicate.parameterName) : factory.createThisTypeNode(), typePredicate.type && nodeBuilder.typeToTypeNode(typePredicate.type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName)! // TODO: GH#18217 ); - const printer = createPrinterWithRemoveComments(); + const printer = createOrReusePrinter({ removeComments: true }); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, predicate, /*sourceFile*/ sourceFile, writer); return writer; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e81f2b090a8ae..289383c99c954 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1343,17 +1343,29 @@ const enum PipelinePhase { Emit, } -/** @internal */ -export const createPrinterWithDefaults = memoize(() => createPrinter({})); +const printerCache = new Map(); +// If updating this, make sure the cache key is updated too. /** @internal */ -export const createPrinterWithRemoveComments = memoize(() => createPrinter({ removeComments: true })); +export type ReusablePrinterOptions = Pick; /** @internal */ -export const createPrinterWithRemoveCommentsNeverAsciiEscape = memoize(() => createPrinter({ removeComments: true, neverAsciiEscape: true })); +export function createOrReusePrinter(o: ReusablePrinterOptions = {}) { + const key = `${keyBool(o.removeComments)}|${keyBool(o.neverAsciiEscape)}|${keyBool(o.omitTrailingSemicolon)}|${keyNum(o.module)}|${keyNum(o.target)}|${keyNum(o.newLine)}`; + let printer = printerCache.get(key); + if (!printer) { + printerCache.set(key, printer = createPrinter(o)); + } + return printer; -/** @internal */ -export const createPrinterWithRemoveCommentsOmitTrailingSemicolon = memoize(() => createPrinter({ removeComments: true, omitTrailingSemicolon: true })); + function keyBool(value: boolean | undefined) { + return value === undefined ? "u" : value ? 1 : 0; + } + + function keyNum(value: number | undefined) { + return value === undefined ? "u" : `${value}`; + } +} export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer { const { diff --git a/src/services/callHierarchy.ts b/src/services/callHierarchy.ts index a2d32e6da717f..fafa979780fbf 100644 --- a/src/services/callHierarchy.ts +++ b/src/services/callHierarchy.ts @@ -14,7 +14,7 @@ import { ClassLikeDeclaration, ClassStaticBlockDeclaration, compareStringsCaseSensitive, - createPrinterWithRemoveCommentsOmitTrailingSemicolon, + createOrReusePrinter, createTextRangeFromNode, createTextSpanFromBounds, createTextSpanFromRange, @@ -245,7 +245,7 @@ function getCallHierarchyItemName(program: Program, node: CallHierarchyDeclarati } if (text === undefined) { // get the text from printing the node on a single line without comments... - const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); + const printer = createOrReusePrinter({ removeComments: true, omitTrailingSemicolon: true }); text = usingSingleLineStringWriter(writer => printer.writeNode(EmitHint.Unspecified, node, node.getSourceFile(), writer)); } return { text, pos: declName.getStart(), end: declName.getEnd() }; diff --git a/src/services/completions.ts b/src/services/completions.ts index 4193e651a037f..b538e0566ef62 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -34,6 +34,7 @@ import { ConstructorDeclaration, ContextFlags, createModuleSpecifierResolutionHost, + createOrReusePrinter, createPackageJsonImportFilter, createPrinter, createSortedArray, @@ -1747,7 +1748,7 @@ function getEntryForObjectLiteralMethodCompletion( insertText = printer.printSnippetList(ListFormat.CommaDelimited | ListFormat.AllowTrailingComma, factory.createNodeArray([method], /*hasTrailingComma*/ true), sourceFile); } - const signaturePrinter = createPrinter({ + const signaturePrinter = createOrReusePrinter({ removeComments: true, module: options.module, target: options.target, diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index 49b113e927554..5f8d9ab7810af 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -2,7 +2,7 @@ import { __String, ArrowFunction, CallExpression, - createPrinterWithRemoveComments, + createOrReusePrinter, Debug, EmitHint, EnumMember, @@ -382,7 +382,7 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { function printTypeInSingleLine(type: Type) { const flags = NodeBuilderFlags.IgnoreErrors | TypeFormatFlags.AllowUniqueESSymbolType | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope; - const printer = createPrinterWithRemoveComments(); + const printer = createOrReusePrinter({ removeComments: true }); return usingSingleLineStringWriter(writer => { const typeNode = checker.typeToTypeNode(type, /*enclosingDeclaration*/ undefined, flags); diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index c8923140c4935..745756503ce21 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -7,7 +7,7 @@ import { CheckFlags, contains, countWhere, - createPrinterWithRemoveComments, + createOrReusePrinter, createTextSpan, createTextSpanFromBounds, createTextSpanFromNode, @@ -659,7 +659,7 @@ function createTypeHelpItems( function getTypeHelpItem(symbol: Symbol, typeParameters: readonly TypeParameter[], checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItem { const typeSymbolDisplay = symbolToDisplayParts(checker, symbol); - const printer = createPrinterWithRemoveComments(); + const printer = createOrReusePrinter({ removeComments: true }); const parameters = typeParameters.map(t => createSignatureHelpParameterForTypeParameter(t, checker, enclosingDeclaration, sourceFile, printer)); const documentation = symbol.getDocumentationComment(checker); @@ -699,7 +699,7 @@ interface SignatureHelpItemInfo { readonly isVariadic: boolean; readonly paramet function itemInfoForTypeParameters(candidateSignature: Signature, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItemInfo[] { const typeParameters = (candidateSignature.target || candidateSignature).typeParameters; - const printer = createPrinterWithRemoveComments(); + const printer = createOrReusePrinter({ removeComments: true }); const parameters = (typeParameters || emptyArray).map(t => createSignatureHelpParameterForTypeParameter(t, checker, enclosingDeclaration, sourceFile, printer)); const thisParameter = candidateSignature.thisParameter ? [checker.symbolToParameterDeclaration(candidateSignature.thisParameter, enclosingDeclaration, signatureHelpNodeBuilderFlags)!] : []; @@ -713,7 +713,7 @@ function itemInfoForTypeParameters(candidateSignature: Signature, checker: TypeC } function itemInfoForParameters(candidateSignature: Signature, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItemInfo[] { - const printer = createPrinterWithRemoveComments(); + const printer = createOrReusePrinter({ removeComments: true }); const typeParameterParts = mapToDisplayParts(writer => { if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) { const args = factory.createNodeArray(candidateSignature.typeParameters.map(p => checker.typeParameterToDeclaration(p, enclosingDeclaration, signatureHelpNodeBuilderFlags)!)); diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index be2e83a48bb2f..a0a9059379d62 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -5,7 +5,7 @@ import { CallExpression, CheckFlags, contains, - createPrinterWithRemoveComments, + createOrReusePrinter, Debug, displayPart, EmitHint, @@ -728,7 +728,7 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ return { displayParts, documentation, symbolKind, tags: tags.length === 0 ? undefined : tags }; function getPrinter() { - return createPrinterWithRemoveComments(); + return createOrReusePrinter({ removeComments: true }); } function prefixNextMeaning() { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 9935d419b2c4b..1a53d38f9dfea 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -30,7 +30,7 @@ import { ConditionalExpression, contains, ContextFlags, - createPrinterWithRemoveCommentsOmitTrailingSemicolon, + createOrReusePrinter, createRange, createScanner, createTextSpan, @@ -2984,7 +2984,7 @@ export function signatureToDisplayParts(typechecker: TypeChecker, signature: Sig export function nodeToDisplayParts(node: Node, enclosingDeclaration: Node): SymbolDisplayPart[] { const file = enclosingDeclaration.getSourceFile(); return mapToDisplayParts(writer => { - const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); + const printer = createOrReusePrinter({ removeComments: true, omitTrailingSemicolon: true }); printer.writeNode(EmitHint.Unspecified, node, file, writer); }); } From 2c3c9c80a97650d7baaaabe3c410e1d12820aad9 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 24 Jan 2023 21:42:14 -0800 Subject: [PATCH 08/16] Use strings --- src/compiler/emitter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 289383c99c954..8e5f87d952f89 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1358,11 +1358,11 @@ export function createOrReusePrinter(o: ReusablePrinterOptions = {}) { } return printer; - function keyBool(value: boolean | undefined) { - return value === undefined ? "u" : value ? 1 : 0; + function keyBool(value: boolean | undefined): string { + return value === undefined ? "u" : value ? "1" : "0"; } - function keyNum(value: number | undefined) { + function keyNum(value: number | undefined): string { return value === undefined ? "u" : `${value}`; } } From bc7ad5d1934989651c1bd98a72c5d4db2daeedcd Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 24 Jan 2023 21:52:03 -0800 Subject: [PATCH 09/16] Use a faster key, this one is twice as fast as the previous --- src/compiler/emitter.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8e5f87d952f89..ce1e523a08f78 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1351,7 +1351,12 @@ export type ReusablePrinterOptions = Pick Date: Tue, 24 Jan 2023 21:54:52 -0800 Subject: [PATCH 10/16] Restore previous formatting --- src/compiler/checker.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 32cf2fa054ef1..1aea0c180e432 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6123,9 +6123,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function symbolToStringWorker(writer: EmitTextWriter) { const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags)!; // TODO: GH#18217 // add neverAsciiEscape for GH#39027 - const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile - ? createOrReusePrinter({ removeComments: true, neverAsciiEscape: true }) - : createOrReusePrinter({ removeComments: true }); + const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile ? createOrReusePrinter({ removeComments: true, neverAsciiEscape: true }) : createOrReusePrinter({ removeComments: true }); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer); return writer; From 82251a3dfde49f6204de5c023dad94bb057b32a4 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 24 Jan 2023 21:59:28 -0800 Subject: [PATCH 11/16] Go a little faster by comparing against undefined --- src/compiler/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ce1e523a08f78..3ff1b6e7830b3 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1358,7 +1358,7 @@ export function createOrReusePrinter(o: ReusablePrinterOptions = {}) { + keyNum(o.target) + keyNum(o.newLine); let printer = printerCache.get(key); - if (!printer) { + if (printer === undefined) { printerCache.set(key, printer = createPrinter(o)); } return printer; From ce0f7ce0221b05879fa7c833f53216cd23176b5d Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:06:51 -0800 Subject: [PATCH 12/16] Revert "Go a little faster by comparing against undefined" This reverts commit 82251a3dfde49f6204de5c023dad94bb057b32a4. --- src/compiler/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3ff1b6e7830b3..ce1e523a08f78 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1358,7 +1358,7 @@ export function createOrReusePrinter(o: ReusablePrinterOptions = {}) { + keyNum(o.target) + keyNum(o.newLine); let printer = printerCache.get(key); - if (printer === undefined) { + if (!printer) { printerCache.set(key, printer = createPrinter(o)); } return printer; From 8f26dc1b2c07cdfc866bfc54626fca8281e483fd Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:06:52 -0800 Subject: [PATCH 13/16] Revert "Restore previous formatting" This reverts commit 521a00ac68a34705523c231122ce03b393f11cdc. --- src/compiler/checker.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1aea0c180e432..32cf2fa054ef1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6123,7 +6123,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function symbolToStringWorker(writer: EmitTextWriter) { const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags)!; // TODO: GH#18217 // add neverAsciiEscape for GH#39027 - const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile ? createOrReusePrinter({ removeComments: true, neverAsciiEscape: true }) : createOrReusePrinter({ removeComments: true }); + const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile + ? createOrReusePrinter({ removeComments: true, neverAsciiEscape: true }) + : createOrReusePrinter({ removeComments: true }); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer); return writer; From efe881c145144cdc77775e4a9dcc81c6e48cd5d9 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:06:53 -0800 Subject: [PATCH 14/16] Revert "Use a faster key, this one is twice as fast as the previous" This reverts commit bc7ad5d1934989651c1bd98a72c5d4db2daeedcd. --- src/compiler/emitter.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ce1e523a08f78..8e5f87d952f89 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1351,12 +1351,7 @@ export type ReusablePrinterOptions = Pick Date: Wed, 25 Jan 2023 11:06:54 -0800 Subject: [PATCH 15/16] Revert "Use strings" This reverts commit 2c3c9c80a97650d7baaaabe3c410e1d12820aad9. --- src/compiler/emitter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8e5f87d952f89..289383c99c954 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1358,11 +1358,11 @@ export function createOrReusePrinter(o: ReusablePrinterOptions = {}) { } return printer; - function keyBool(value: boolean | undefined): string { - return value === undefined ? "u" : value ? "1" : "0"; + function keyBool(value: boolean | undefined) { + return value === undefined ? "u" : value ? 1 : 0; } - function keyNum(value: number | undefined): string { + function keyNum(value: number | undefined) { return value === undefined ? "u" : `${value}`; } } From 4d7ab4031f8c7c227269c089928e601ac08e7772 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:06:55 -0800 Subject: [PATCH 16/16] Revert "Use a cache with a key on subset of options" This reverts commit 28dfb919a032fdea9ac9b0d41711369b1628d4bc. --- src/compiler/checker.ts | 16 +++++++++------- src/compiler/emitter.ts | 24 ++++++------------------ src/services/callHierarchy.ts | 4 ++-- src/services/completions.ts | 3 +-- src/services/inlayHints.ts | 4 ++-- src/services/signatureHelp.ts | 8 ++++---- src/services/symbolDisplay.ts | 4 ++-- src/services/utilities.ts | 4 ++-- 8 files changed, 28 insertions(+), 39 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 32cf2fa054ef1..985893023a30b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -111,7 +111,10 @@ import { createGetCanonicalFileName, createGetSymbolWalker, createModeAwareCacheKey, - createOrReusePrinter, + createPrinterWithDefaults, + createPrinterWithRemoveComments, + createPrinterWithRemoveCommentsNeverAsciiEscape, + createPrinterWithRemoveCommentsOmitTrailingSemicolon, createPropertyNameNodeForIdentifierOrLiteral, createSymbolTable, createTextWriter, @@ -6124,8 +6127,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags)!; // TODO: GH#18217 // add neverAsciiEscape for GH#39027 const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile - ? createOrReusePrinter({ removeComments: true, neverAsciiEscape: true }) - : createOrReusePrinter({ removeComments: true }); + ? createPrinterWithRemoveCommentsNeverAsciiEscape() + : createPrinterWithRemoveComments(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer); return writer; @@ -6144,7 +6147,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { sigOutput = kind === SignatureKind.Construct ? SyntaxKind.ConstructSignature : SyntaxKind.CallSignature; } const sig = nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); - const printer = createOrReusePrinter({ removeComments: true, omitTrailingSemicolon: true }); + const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonDeferringWriter(writer)); // TODO: GH#18217 return writer; @@ -6157,8 +6160,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (typeNode === undefined) return Debug.fail("should always get typenode"); // The unresolved type gets a synthesized comment on `any` to hint to users that it's not a plain `any`. // Otherwise, we always strip comments out. - const options = { removeComments: type !== unresolvedType }; - const printer = createOrReusePrinter(options); + const printer = type !== unresolvedType ? createPrinterWithRemoveComments() : createPrinterWithDefaults(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer); const result = writer.getText(); @@ -9638,7 +9640,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? factory.createIdentifier(typePredicate.parameterName) : factory.createThisTypeNode(), typePredicate.type && nodeBuilder.typeToTypeNode(typePredicate.type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName)! // TODO: GH#18217 ); - const printer = createOrReusePrinter({ removeComments: true }); + const printer = createPrinterWithRemoveComments(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, predicate, /*sourceFile*/ sourceFile, writer); return writer; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 289383c99c954..e81f2b090a8ae 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1343,29 +1343,17 @@ const enum PipelinePhase { Emit, } -const printerCache = new Map(); - -// If updating this, make sure the cache key is updated too. /** @internal */ -export type ReusablePrinterOptions = Pick; +export const createPrinterWithDefaults = memoize(() => createPrinter({})); /** @internal */ -export function createOrReusePrinter(o: ReusablePrinterOptions = {}) { - const key = `${keyBool(o.removeComments)}|${keyBool(o.neverAsciiEscape)}|${keyBool(o.omitTrailingSemicolon)}|${keyNum(o.module)}|${keyNum(o.target)}|${keyNum(o.newLine)}`; - let printer = printerCache.get(key); - if (!printer) { - printerCache.set(key, printer = createPrinter(o)); - } - return printer; +export const createPrinterWithRemoveComments = memoize(() => createPrinter({ removeComments: true })); - function keyBool(value: boolean | undefined) { - return value === undefined ? "u" : value ? 1 : 0; - } +/** @internal */ +export const createPrinterWithRemoveCommentsNeverAsciiEscape = memoize(() => createPrinter({ removeComments: true, neverAsciiEscape: true })); - function keyNum(value: number | undefined) { - return value === undefined ? "u" : `${value}`; - } -} +/** @internal */ +export const createPrinterWithRemoveCommentsOmitTrailingSemicolon = memoize(() => createPrinter({ removeComments: true, omitTrailingSemicolon: true })); export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer { const { diff --git a/src/services/callHierarchy.ts b/src/services/callHierarchy.ts index fafa979780fbf..a2d32e6da717f 100644 --- a/src/services/callHierarchy.ts +++ b/src/services/callHierarchy.ts @@ -14,7 +14,7 @@ import { ClassLikeDeclaration, ClassStaticBlockDeclaration, compareStringsCaseSensitive, - createOrReusePrinter, + createPrinterWithRemoveCommentsOmitTrailingSemicolon, createTextRangeFromNode, createTextSpanFromBounds, createTextSpanFromRange, @@ -245,7 +245,7 @@ function getCallHierarchyItemName(program: Program, node: CallHierarchyDeclarati } if (text === undefined) { // get the text from printing the node on a single line without comments... - const printer = createOrReusePrinter({ removeComments: true, omitTrailingSemicolon: true }); + const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); text = usingSingleLineStringWriter(writer => printer.writeNode(EmitHint.Unspecified, node, node.getSourceFile(), writer)); } return { text, pos: declName.getStart(), end: declName.getEnd() }; diff --git a/src/services/completions.ts b/src/services/completions.ts index b538e0566ef62..4193e651a037f 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -34,7 +34,6 @@ import { ConstructorDeclaration, ContextFlags, createModuleSpecifierResolutionHost, - createOrReusePrinter, createPackageJsonImportFilter, createPrinter, createSortedArray, @@ -1748,7 +1747,7 @@ function getEntryForObjectLiteralMethodCompletion( insertText = printer.printSnippetList(ListFormat.CommaDelimited | ListFormat.AllowTrailingComma, factory.createNodeArray([method], /*hasTrailingComma*/ true), sourceFile); } - const signaturePrinter = createOrReusePrinter({ + const signaturePrinter = createPrinter({ removeComments: true, module: options.module, target: options.target, diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index 5f8d9ab7810af..49b113e927554 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -2,7 +2,7 @@ import { __String, ArrowFunction, CallExpression, - createOrReusePrinter, + createPrinterWithRemoveComments, Debug, EmitHint, EnumMember, @@ -382,7 +382,7 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { function printTypeInSingleLine(type: Type) { const flags = NodeBuilderFlags.IgnoreErrors | TypeFormatFlags.AllowUniqueESSymbolType | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope; - const printer = createOrReusePrinter({ removeComments: true }); + const printer = createPrinterWithRemoveComments(); return usingSingleLineStringWriter(writer => { const typeNode = checker.typeToTypeNode(type, /*enclosingDeclaration*/ undefined, flags); diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 745756503ce21..c8923140c4935 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -7,7 +7,7 @@ import { CheckFlags, contains, countWhere, - createOrReusePrinter, + createPrinterWithRemoveComments, createTextSpan, createTextSpanFromBounds, createTextSpanFromNode, @@ -659,7 +659,7 @@ function createTypeHelpItems( function getTypeHelpItem(symbol: Symbol, typeParameters: readonly TypeParameter[], checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItem { const typeSymbolDisplay = symbolToDisplayParts(checker, symbol); - const printer = createOrReusePrinter({ removeComments: true }); + const printer = createPrinterWithRemoveComments(); const parameters = typeParameters.map(t => createSignatureHelpParameterForTypeParameter(t, checker, enclosingDeclaration, sourceFile, printer)); const documentation = symbol.getDocumentationComment(checker); @@ -699,7 +699,7 @@ interface SignatureHelpItemInfo { readonly isVariadic: boolean; readonly paramet function itemInfoForTypeParameters(candidateSignature: Signature, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItemInfo[] { const typeParameters = (candidateSignature.target || candidateSignature).typeParameters; - const printer = createOrReusePrinter({ removeComments: true }); + const printer = createPrinterWithRemoveComments(); const parameters = (typeParameters || emptyArray).map(t => createSignatureHelpParameterForTypeParameter(t, checker, enclosingDeclaration, sourceFile, printer)); const thisParameter = candidateSignature.thisParameter ? [checker.symbolToParameterDeclaration(candidateSignature.thisParameter, enclosingDeclaration, signatureHelpNodeBuilderFlags)!] : []; @@ -713,7 +713,7 @@ function itemInfoForTypeParameters(candidateSignature: Signature, checker: TypeC } function itemInfoForParameters(candidateSignature: Signature, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItemInfo[] { - const printer = createOrReusePrinter({ removeComments: true }); + const printer = createPrinterWithRemoveComments(); const typeParameterParts = mapToDisplayParts(writer => { if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) { const args = factory.createNodeArray(candidateSignature.typeParameters.map(p => checker.typeParameterToDeclaration(p, enclosingDeclaration, signatureHelpNodeBuilderFlags)!)); diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index a0a9059379d62..be2e83a48bb2f 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -5,7 +5,7 @@ import { CallExpression, CheckFlags, contains, - createOrReusePrinter, + createPrinterWithRemoveComments, Debug, displayPart, EmitHint, @@ -728,7 +728,7 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ return { displayParts, documentation, symbolKind, tags: tags.length === 0 ? undefined : tags }; function getPrinter() { - return createOrReusePrinter({ removeComments: true }); + return createPrinterWithRemoveComments(); } function prefixNextMeaning() { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 1a53d38f9dfea..9935d419b2c4b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -30,7 +30,7 @@ import { ConditionalExpression, contains, ContextFlags, - createOrReusePrinter, + createPrinterWithRemoveCommentsOmitTrailingSemicolon, createRange, createScanner, createTextSpan, @@ -2984,7 +2984,7 @@ export function signatureToDisplayParts(typechecker: TypeChecker, signature: Sig export function nodeToDisplayParts(node: Node, enclosingDeclaration: Node): SymbolDisplayPart[] { const file = enclosingDeclaration.getSourceFile(); return mapToDisplayParts(writer => { - const printer = createOrReusePrinter({ removeComments: true, omitTrailingSemicolon: true }); + const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); printer.writeNode(EmitHint.Unspecified, node, file, writer); }); }