|
2 | 2 | __String,
|
3 | 3 | AccessExpression,
|
4 | 4 | AccessorDeclaration,
|
| 5 | + addEmitFlags, |
5 | 6 | addRange,
|
6 | 7 | affectsDeclarationPathOptionDeclarations,
|
7 | 8 | affectsEmitOptionDeclarations,
|
@@ -511,6 +512,8 @@ import {
|
511 | 512 | ScriptTarget,
|
512 | 513 | semanticDiagnosticsOptionDeclarations,
|
513 | 514 | SetAccessorDeclaration,
|
| 515 | + setOriginalNode, |
| 516 | + setTextRange, |
514 | 517 | ShorthandPropertyAssignment,
|
515 | 518 | shouldAllowImportingTsExtension,
|
516 | 519 | Signature,
|
@@ -592,6 +595,7 @@ import {
|
592 | 595 | VariableDeclarationList,
|
593 | 596 | VariableLikeDeclaration,
|
594 | 597 | VariableStatement,
|
| 598 | + visitEachChild, |
595 | 599 | WhileStatement,
|
596 | 600 | WithStatement,
|
597 | 601 | WrappedExpression,
|
@@ -12208,3 +12212,121 @@ export function getOptionsSyntaxByValue(optionsObject: ObjectLiteralExpression |
|
12208 | 12212 | export function forEachOptionsSyntaxByName<T>(optionsObject: ObjectLiteralExpression | undefined, name: string, callback: (prop: PropertyAssignment) => T | undefined): T | undefined {
|
12209 | 12213 | return forEachPropertyAssignment(optionsObject, name, callback);
|
12210 | 12214 | }
|
| 12215 | + |
| 12216 | +/** |
| 12217 | + * Creates a deep, memberwise clone of a node with no source map location. |
| 12218 | + * |
| 12219 | + * WARNING: This is an expensive operation and is only intended to be used in refactorings |
| 12220 | + * and code fixes (because those are triggered by explicit user actions). |
| 12221 | + * |
| 12222 | + * @internal |
| 12223 | + */ |
| 12224 | +// Moved here to compiler utilities for usage in node builder for quickinfo. |
| 12225 | +export function getSynthesizedDeepClone<T extends Node | undefined>(node: T, includeTrivia = true): T { |
| 12226 | + const clone = node && getSynthesizedDeepCloneWorker(node); |
| 12227 | + if (clone && !includeTrivia) suppressLeadingAndTrailingTrivia(clone); |
| 12228 | + return setParentRecursive(clone, /*incremental*/ false); |
| 12229 | +} |
| 12230 | + |
| 12231 | +/** @internal */ |
| 12232 | +export function getSynthesizedDeepCloneWithReplacements<T extends Node>( |
| 12233 | + node: T, |
| 12234 | + includeTrivia: boolean, |
| 12235 | + replaceNode: (node: Node) => Node | undefined, |
| 12236 | +): T { |
| 12237 | + let clone = replaceNode(node); |
| 12238 | + if (clone) { |
| 12239 | + setOriginalNode(clone, node); |
| 12240 | + } |
| 12241 | + else { |
| 12242 | + clone = getSynthesizedDeepCloneWorker(node as NonNullable<T>, replaceNode); |
| 12243 | + } |
| 12244 | + |
| 12245 | + if (clone && !includeTrivia) suppressLeadingAndTrailingTrivia(clone); |
| 12246 | + return clone as T; |
| 12247 | +} |
| 12248 | + |
| 12249 | +function getSynthesizedDeepCloneWorker<T extends Node>(node: T, replaceNode?: (node: Node) => Node | undefined): T { |
| 12250 | + const nodeClone: <T extends Node>(n: T) => T = replaceNode |
| 12251 | + ? n => getSynthesizedDeepCloneWithReplacements(n, /*includeTrivia*/ true, replaceNode) |
| 12252 | + : getSynthesizedDeepClone; |
| 12253 | + const nodesClone: <T extends Node>(ns: NodeArray<T> | undefined) => NodeArray<T> | undefined = replaceNode |
| 12254 | + ? ns => ns && getSynthesizedDeepClonesWithReplacements(ns, /*includeTrivia*/ true, replaceNode) |
| 12255 | + : ns => ns && getSynthesizedDeepClones(ns); |
| 12256 | + const visited = visitEachChild(node, nodeClone, /*context*/ undefined, nodesClone, nodeClone); |
| 12257 | + |
| 12258 | + if (visited === node) { |
| 12259 | + // This only happens for leaf nodes - internal nodes always see their children change. |
| 12260 | + const clone = isStringLiteral(node) ? setOriginalNode(factory.createStringLiteralFromNode(node), node) as Node as T : |
| 12261 | + isNumericLiteral(node) ? setOriginalNode(factory.createNumericLiteral(node.text, node.numericLiteralFlags), node) as Node as T : |
| 12262 | + factory.cloneNode(node); |
| 12263 | + return setTextRange(clone, node); |
| 12264 | + } |
| 12265 | + |
| 12266 | + // PERF: As an optimization, rather than calling factory.cloneNode, we'll update |
| 12267 | + // the new node created by visitEachChild with the extra changes factory.cloneNode |
| 12268 | + // would have made. |
| 12269 | + (visited as Mutable<T>).parent = undefined!; |
| 12270 | + return visited; |
| 12271 | +} |
| 12272 | + |
| 12273 | +/** @internal */ |
| 12274 | +export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T>, includeTrivia?: boolean): NodeArray<T>; |
| 12275 | +/** @internal */ |
| 12276 | +export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T> | undefined, includeTrivia?: boolean): NodeArray<T> | undefined; |
| 12277 | +/** @internal */ |
| 12278 | +export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T> | undefined, includeTrivia = true): NodeArray<T> | undefined { |
| 12279 | + if (nodes) { |
| 12280 | + const cloned = factory.createNodeArray(nodes.map(n => getSynthesizedDeepClone(n, includeTrivia)), nodes.hasTrailingComma); |
| 12281 | + setTextRange(cloned, nodes); |
| 12282 | + return cloned; |
| 12283 | + } |
| 12284 | + return nodes; |
| 12285 | +} |
| 12286 | + |
| 12287 | +/** @internal */ |
| 12288 | +export function getSynthesizedDeepClonesWithReplacements<T extends Node>( |
| 12289 | + nodes: NodeArray<T>, |
| 12290 | + includeTrivia: boolean, |
| 12291 | + replaceNode: (node: Node) => Node | undefined, |
| 12292 | +): NodeArray<T> { |
| 12293 | + return factory.createNodeArray(nodes.map(n => getSynthesizedDeepCloneWithReplacements(n, includeTrivia, replaceNode)), nodes.hasTrailingComma); |
| 12294 | +} |
| 12295 | + |
| 12296 | +/** |
| 12297 | + * Sets EmitFlags to suppress leading and trailing trivia on the node. |
| 12298 | + * |
| 12299 | + * @internal |
| 12300 | + */ |
| 12301 | +export function suppressLeadingAndTrailingTrivia(node: Node): void { |
| 12302 | + suppressLeadingTrivia(node); |
| 12303 | + suppressTrailingTrivia(node); |
| 12304 | +} |
| 12305 | + |
| 12306 | +/** |
| 12307 | + * Sets EmitFlags to suppress leading trivia on the node. |
| 12308 | + * |
| 12309 | + * @internal |
| 12310 | + */ |
| 12311 | +export function suppressLeadingTrivia(node: Node): void { |
| 12312 | + addEmitFlagsRecursively(node, EmitFlags.NoLeadingComments, getFirstChild); |
| 12313 | +} |
| 12314 | + |
| 12315 | +/** |
| 12316 | + * Sets EmitFlags to suppress trailing trivia on the node. |
| 12317 | + * |
| 12318 | + * @internal @knipignore |
| 12319 | + */ |
| 12320 | +export function suppressTrailingTrivia(node: Node): void { |
| 12321 | + addEmitFlagsRecursively(node, EmitFlags.NoTrailingComments, getLastChild); |
| 12322 | +} |
| 12323 | + |
| 12324 | +function addEmitFlagsRecursively(node: Node, flag: EmitFlags, getChild: (n: Node) => Node | undefined) { |
| 12325 | + addEmitFlags(node, flag); |
| 12326 | + const child = getChild(node); |
| 12327 | + if (child) addEmitFlagsRecursively(child, flag, getChild); |
| 12328 | +} |
| 12329 | + |
| 12330 | +function getFirstChild(node: Node): Node | undefined { |
| 12331 | + return forEachChild(node, child => child); |
| 12332 | +} |
0 commit comments