UNPKG

@rightcapital/phpdoc-parser

Version:

TypeScript version of PHPDoc parser with support for intersection types and generics

613 lines (612 loc) • 31.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Printer = void 0; const base_node_1 = require("../ast/base-node"); const const_expr_array_node_1 = require("../ast/const-expr/const-expr-array-node"); const const_expr_node_1 = require("../ast/const-expr/const-expr-node"); const assert_tag_method_value_node_1 = require("../ast/php-doc/assert-tag-method-value-node"); const assert_tag_property_value_node_1 = require("../ast/php-doc/assert-tag-property-value-node"); const assert_tag_value_node_1 = require("../ast/php-doc/assert-tag-value-node"); const extends_tag_value_node_1 = require("../ast/php-doc/extends-tag-value-node"); const implements_tag_value_node_1 = require("../ast/php-doc/implements-tag-value-node"); const method_tag_value_node_1 = require("../ast/php-doc/method-tag-value-node"); const method_tag_value_parameter_node_1 = require("../ast/php-doc/method-tag-value-parameter-node"); const mixin_tag_value_node_1 = require("../ast/php-doc/mixin-tag-value-node"); const param_out_tag_value_node_1 = require("../ast/php-doc/param-out-tag-value-node"); const param_tag_value_node_1 = require("../ast/php-doc/param-tag-value-node"); const php_doc_node_1 = require("../ast/php-doc/php-doc-node"); const php_doc_tag_node_1 = require("../ast/php-doc/php-doc-tag-node"); const php_doc_tag_value_node_1 = require("../ast/php-doc/php-doc-tag-value-node"); const php_doc_text_node_1 = require("../ast/php-doc/php-doc-text-node"); const property_tag_value_node_1 = require("../ast/php-doc/property-tag-value-node"); const return_tag_value_node_1 = require("../ast/php-doc/return-tag-value-node"); const self_out_tag_value_node_1 = require("../ast/php-doc/self-out-tag-value-node"); const template_tag_value_node_1 = require("../ast/php-doc/template-tag-value-node"); const throws_tag_value_node_1 = require("../ast/php-doc/throws-tag-value-node"); const type_alias_import_tag_value_node_1 = require("../ast/php-doc/type-alias-import-tag-value-node"); const type_alias_tag_value_node_1 = require("../ast/php-doc/type-alias-tag-value-node"); const uses_tag_value_node_1 = require("../ast/php-doc/uses-tag-value-node"); const var_tag_value_node_1 = require("../ast/php-doc/var-tag-value-node"); const array_shape_item_node_1 = require("../ast/type/array-shape-item-node"); const array_shape_node_1 = require("../ast/type/array-shape-node"); const array_type_node_1 = require("../ast/type/array-type-node"); const callable_type_node_1 = require("../ast/type/callable-type-node"); const callable_type_parameter_node_1 = require("../ast/type/callable-type-parameter-node"); const conditional_type_for_parameter_node_1 = require("../ast/type/conditional-type-for-parameter-node"); const conditional_type_node_1 = require("../ast/type/conditional-type-node"); const const_type_node_1 = require("../ast/type/const-type-node"); const generic_type_node_1 = require("../ast/type/generic-type-node"); const identifier_type_node_1 = require("../ast/type/identifier-type-node"); const intersection_type_node_1 = require("../ast/type/intersection-type-node"); const invalid_type_node_1 = require("../ast/type/invalid-type-node"); const nullable_type_node_1 = require("../ast/type/nullable-type-node"); const object_shape_item_node_1 = require("../ast/type/object-shape-item-node"); const object_shape_node_1 = require("../ast/type/object-shape-node"); const offset_access_type_node_1 = require("../ast/type/offset-access-type-node"); const this_type_node_1 = require("../ast/type/this-type-node"); const type_node_1 = require("../ast/type/type-node"); const union_type_node_1 = require("../ast/type/union-type-node"); const types_1 = require("../ast/types"); const lexer_1 = require("../lexer/lexer"); const diff_elem_1 = require("./diff-elem"); const differ_1 = require("./differ"); class Printer { constructor() { this.listInsertionMap = { [`${php_doc_node_1.PhpDocNode.name}->children`]: '\n * ', [`${union_type_node_1.UnionTypeNode.name}->types`]: '|', [`${intersection_type_node_1.IntersectionTypeNode.name}->types`]: '&', [`${array_shape_node_1.ArrayShapeNode.name}->items`]: ', ', [`${object_shape_node_1.ObjectShapeNode.name}->items`]: ', ', [`${callable_type_node_1.CallableTypeNode.name}->parameters`]: ', ', [`${generic_type_node_1.GenericTypeNode.name}->genericTypes`]: ', ', [`${const_expr_array_node_1.ConstExprArrayNode.name}->items`]: ', ', [`${method_tag_value_node_1.MethodTagValueNode.name}->parameters`]: ', ', }; this.emptyListInsertionMap = { [`${callable_type_node_1.CallableTypeNode.name}->parameters`]: ['(', '', ''], [`${array_shape_node_1.ArrayShapeNode.name}->items`]: ['{', '', '}'], [`${object_shape_node_1.ObjectShapeNode.name}->items`]: ['{', '', '}'], }; this.parenthesesMap = { [`${callable_type_node_1.CallableTypeNode.name}->returnType`]: [ callable_type_node_1.CallableTypeNode.name, union_type_node_1.UnionTypeNode.name, intersection_type_node_1.IntersectionTypeNode.name, ], [`${array_type_node_1.ArrayTypeNode.name}->type`]: [ callable_type_node_1.CallableTypeNode.name, union_type_node_1.UnionTypeNode.name, intersection_type_node_1.IntersectionTypeNode.name, const_type_node_1.ConstTypeNode.name, nullable_type_node_1.NullableTypeNode.name, ], [`${offset_access_type_node_1.OffsetAccessTypeNode.name}->type`]: [ callable_type_node_1.CallableTypeNode.name, union_type_node_1.UnionTypeNode.name, intersection_type_node_1.IntersectionTypeNode.name, const_type_node_1.ConstTypeNode.name, nullable_type_node_1.NullableTypeNode.name, ], }; this.parenthesesListMap = { [`${intersection_type_node_1.IntersectionTypeNode.name}->types`]: [ intersection_type_node_1.IntersectionTypeNode.name, union_type_node_1.UnionTypeNode.name, nullable_type_node_1.NullableTypeNode.name, ], [`${union_type_node_1.UnionTypeNode.name}->types`]: [ intersection_type_node_1.IntersectionTypeNode.name, union_type_node_1.UnionTypeNode.name, nullable_type_node_1.NullableTypeNode.name, ], }; } printFormatPreserving(node, originalNode, originalTokens) { this.differ = new differ_1.Differ((a, b) => { if (a instanceof base_node_1.BaseNode && b instanceof base_node_1.BaseNode) { return a === b.getAttribute(types_1.Attribute.ORIGINAL_NODE); } return false; }); let tokenIndex = 0; let result; [result, tokenIndex] = this.printArrayFormatPreserving(node.children, originalNode.children, originalTokens, tokenIndex, php_doc_node_1.PhpDocNode.name, 'children'); if (result !== null) { return (result + originalTokens.getContentBetween(tokenIndex, originalTokens.getTokenCount())); } return this.print(node); } print(node) { if (node instanceof php_doc_node_1.PhpDocNode) { return `/**\n *${node.children .map((child) => { return ` ${this.print(child)}`; }) .join('\n *')}\n */`; } if (node instanceof php_doc_text_node_1.PhpDocTextNode) { return node.text; } if (node instanceof php_doc_tag_node_1.PhpDocTagNode) { return `${node.name} ${this.print(node.value)}`.trim(); } if (node instanceof php_doc_tag_value_node_1.PhpDocTagValueNode) { return this.printTagValue(node); } if (node instanceof type_node_1.TypeNode) { return this.printType(node); } if (node instanceof const_expr_node_1.ConstExprNode) { return this.printConstExpr(node); } if (node instanceof method_tag_value_parameter_node_1.MethodTagValueParameterNode) { const type = node.type ? `${this.print(node.type)} ` : ''; const isReference = node.isReference ? '&' : ''; const isVariadic = node.isVariadic ? '...' : ''; const defaultValue = node.defaultValue !== null ? ` = ${this.print(node.defaultValue)}` : ''; return `${type}${isReference}${isVariadic}${node.parameterName}${defaultValue}`; } if (node instanceof callable_type_parameter_node_1.CallableTypeParameterNode) { const type = `${this.print(node.type)} `; const isReference = node.isReference ? '&' : ''; const isVariadic = node.isVariadic ? '...' : ''; const isOptional = node.isOptional ? '=' : ''; return (`${type}${isReference}${isVariadic}${node.parameterName}`.trim() + isOptional); } throw new Error(`Unknown node type ${node.getNodeType()}`); } printTagValue(node) { if (node instanceof assert_tag_method_value_node_1.AssertTagMethodValueNode) { const isNegated = node.isNegated ? '!' : ''; const isEquality = node.isEquality ? '=' : ''; const type = this.printType(node.type); return `${isNegated}${isEquality}${type} ${node.parameter}->${node.method}() ${node.description}`.trim(); } if (node instanceof assert_tag_property_value_node_1.AssertTagPropertyValueNode) { const isNegated = node.isNegated ? '!' : ''; const isEquality = node.isEquality ? '=' : ''; const type = this.printType(node.type); return `${isNegated}${isEquality}${type} ${node.parameter}->${node.property} ${node.description}`.trim(); } if (node instanceof assert_tag_value_node_1.AssertTagValueNode) { const isNegated = node.isNegated ? '!' : ''; const isEquality = node.isEquality ? '=' : ''; const type = this.printType(node.type); return `${isNegated}${isEquality}${type} ${node.parameter} ${node.description}`.trim(); } if (node instanceof extends_tag_value_node_1.ExtendsTagValueNode || node instanceof implements_tag_value_node_1.ImplementsTagValueNode) { const type = this.printType(node.type); return `${type} ${node.description}`.trim(); } if (node instanceof method_tag_value_node_1.MethodTagValueNode) { const staticValue = node.isStatic ? 'static ' : ''; const returnType = node.returnType !== null ? `${this.printType(node.returnType)} ` : ''; const parameters = node.parameters .map((parameter) => { return this.print(parameter); }) .join(', '); const description = node.description !== '' ? ` ${node.description}` : ''; const templateTypes = node.templateTypes.length > 0 ? `<${node.templateTypes .map((templateTag) => { return this.print(templateTag); }) .join(', ')}>` : ''; return `${staticValue}${returnType}${node.methodName}${templateTypes}(${parameters})${description}`; } if (node instanceof mixin_tag_value_node_1.MixinTagValueNode) { const type = this.printType(node.type); return `${type} ${node.description}`.trim(); } if (node instanceof param_out_tag_value_node_1.ParamOutTagValueNode) { const type = this.printType(node.type); return `${type} ${node.parameterName} ${node.description}`.trim(); } if (node instanceof param_tag_value_node_1.ParamTagValueNode) { const reference = node.isReference ? '&' : ''; const variadic = node.isVariadic ? '...' : ''; const type = this.printType(node.type); return `${type} ${reference}${variadic}${node.parameterName} ${node.description}`.trim(); } if (node instanceof property_tag_value_node_1.PropertyTagValueNode) { const type = this.printType(node.type); return `${type} ${node.propertyName} ${node.description}`.trim(); } if (node instanceof return_tag_value_node_1.ReturnTagValueNode) { const type = this.printType(node.type); return `${type} ${node.description}`.trim(); } if (node instanceof self_out_tag_value_node_1.SelfOutTagValueNode) { const type = this.printType(node.type); return `${type} ${node.description}`.trim(); } if (node instanceof template_tag_value_node_1.TemplateTagValueNode) { const bound = node.bound !== null ? ` of ${this.printType(node.bound)}` : ''; const defaultValue = node.defaultTypeNode !== null ? ` = ${this.printType(node.defaultTypeNode)}` : ''; return `${node.name}${bound}${defaultValue} ${node.description}`.trim(); } if (node instanceof throws_tag_value_node_1.ThrowsTagValueNode) { const type = this.printType(node.type); return `${type} ${node.description}`.trim(); } if (node instanceof type_alias_import_tag_value_node_1.TypeAliasImportTagValueNode) { return `${node.importedAlias} from ${this.printType(node.importedFrom)}${(node.importedAs !== null ? ` as ${node.importedAs}` : '').trim()}`; } if (node instanceof type_alias_tag_value_node_1.TypeAliasTagValueNode) { const type = this.printType(node.type); return `${node.alias} ${type}`.trim(); } if (node instanceof uses_tag_value_node_1.UsesTagValueNode) { const type = this.printType(node.type); return `${type} ${node.description}`.trim(); } if (node instanceof var_tag_value_node_1.VarTagValueNode) { const type = this.printType(node.type); return `${type} ${node.variableName} ${node.description}`.trim(); } return node.toString(); } printType(node) { if (node instanceof array_shape_node_1.ArrayShapeNode) { const items = node.items.map((item) => this.printType(item)); if (!node.sealed) { items.push('...'); } return `${node.kind}{${items.join(', ')}}`; } if (node instanceof array_shape_item_node_1.ArrayShapeItemNode) { if (node.keyName !== null) { return `${this.print(node.keyName)}${node.optional ? '?' : ''}: ${this.printType(node.valueType)}`; } return this.printType(node.valueType); } if (node instanceof array_type_node_1.ArrayTypeNode) { return `${this.printOffsetAccessType(node.type)}[]`; } if (node instanceof callable_type_node_1.CallableTypeNode) { const returnType = node.returnType instanceof callable_type_node_1.CallableTypeNode || node.returnType instanceof union_type_node_1.UnionTypeNode || node.returnType instanceof intersection_type_node_1.IntersectionTypeNode ? this.wrapInParentheses(node.returnType) : this.printType(node.returnType); const parameters = node.parameters .map((parameter) => this.print(parameter)) .join(', '); return `${node.identifier.toString()}(${parameters}): ${returnType}`; } if (node instanceof conditional_type_for_parameter_node_1.ConditionalTypeForParameterNode) { return `(${node.parameterName} ${node.negated ? 'is not' : 'is'} ${this.printType(node.targetType)} ? ${this.printType(node.ifCondition)} : ${this.printType(node.elseCondition)})`; } if (node instanceof conditional_type_node_1.ConditionalTypeNode) { return `(${this.printType(node.subjectType)} ${node.negated ? 'is not' : 'is'} ${this.printType(node.targetType)} ? ${this.printType(node.ifType)} : ${this.printType(node.elseType)})`; } if (node instanceof const_type_node_1.ConstTypeNode) { return this.printConstExpr(node.constExpr); } if (node instanceof generic_type_node_1.GenericTypeNode) { const genericTypes = node.genericTypes.map((type, index) => { var _a; const variance = (_a = node.variances[index]) !== null && _a !== void 0 ? _a : generic_type_node_1.GenericTypeNode.VARIANCE_INVARIANT; if (variance === generic_type_node_1.GenericTypeNode.VARIANCE_INVARIANT) { return this.printType(type); } if (variance === generic_type_node_1.GenericTypeNode.VARIANCE_BIVARIANT) { return '*'; } return `${variance} ${this.print(type)}`; }); return `${node.type.toString()}<${genericTypes.join(', ')}>`; } if (node instanceof identifier_type_node_1.IdentifierTypeNode) { return node.name; } if (node instanceof intersection_type_node_1.IntersectionTypeNode || node instanceof union_type_node_1.UnionTypeNode) { const items = node.types.map((type) => { if (type instanceof intersection_type_node_1.IntersectionTypeNode || type instanceof union_type_node_1.UnionTypeNode || type instanceof nullable_type_node_1.NullableTypeNode) { return this.wrapInParentheses(type); } return this.printType(type); }); return items.join(node instanceof intersection_type_node_1.IntersectionTypeNode ? '&' : '|'); } if (node instanceof invalid_type_node_1.InvalidTypeNode) { return node.toString(); } if (node instanceof nullable_type_node_1.NullableTypeNode) { if (node.type instanceof intersection_type_node_1.IntersectionTypeNode || node.type instanceof union_type_node_1.UnionTypeNode) { return `?(${this.printType(node.type)})`; } return `?${this.printType(node.type)}`; } if (node instanceof object_shape_node_1.ObjectShapeNode) { const items = node.items.map((item) => this.printType(item)); return `object{${items.join(', ')}}`; } if (node instanceof object_shape_item_node_1.ObjectShapeItemNode) { if (node.keyName !== null) { return `${this.print(node.keyName)}${node.optional ? '?' : ''}: ${this.printType(node.valueType)}`; } return this.printType(node.valueType); } if (node instanceof offset_access_type_node_1.OffsetAccessTypeNode) { return `${this.printOffsetAccessType(node.type)}[${this.printType(node.offset)}]`; } if (node instanceof this_type_node_1.ThisTypeNode) { return node.toString(); } throw new Error(`Unknown type ${node.getNodeType()}`); } wrapInParentheses(node) { return `(${this.printType(node)})`; } printOffsetAccessType(type) { if (type instanceof callable_type_node_1.CallableTypeNode || type instanceof union_type_node_1.UnionTypeNode || type instanceof intersection_type_node_1.IntersectionTypeNode || type instanceof const_type_node_1.ConstTypeNode || type instanceof nullable_type_node_1.NullableTypeNode) { return this.wrapInParentheses(type); } return this.printType(type); } printConstExpr(node) { return node.toString(); } printArrayFormatPreserving(nodes, originalNodes, originalTokens, tokenIndex, parentNodeClassName, subNodeName) { var _a, _b, _c, _d, _e; const diff = this.differ.diffWithReplacements(originalNodes, nodes); const mapKey = `${parentNodeClassName}->${subNodeName}`; let insertStr = (_a = this.listInsertionMap[mapKey]) !== null && _a !== void 0 ? _a : null; let result = ''; let beforeFirstKeepOrReplace = true; let delayedAdd = []; let insertNewline = false; const [isMultiline, beforeAsteriskIndent, afterAsteriskIndent] = this.isMultiline(tokenIndex, originalNodes, originalTokens); if (insertStr === '\n * ') { insertStr = `\n${beforeAsteriskIndent}*${afterAsteriskIndent}`; } for (let i = 0; i < diff.length; i++) { const diffElem = diff[i]; const newNode = diffElem.new; const originalNode = diffElem.old; if (diffElem.type === diff_elem_1.DiffElemType.KEEP || diffElem.type === diff_elem_1.DiffElemType.REPLACE) { beforeFirstKeepOrReplace = false; if (!(newNode instanceof base_node_1.BaseNode) || !(originalNode instanceof base_node_1.BaseNode)) { return null; } const itemStartPos = originalNode.getAttribute(types_1.Attribute.START_INDEX); const itemEndPos = originalNode.getAttribute(types_1.Attribute.END_INDEX); if (itemStartPos < 0 || itemEndPos < 0 || itemStartPos < tokenIndex) { throw new Error('Invalid position'); } result += originalTokens.getContentBetween(tokenIndex, itemStartPos); if (delayedAdd.length > 0) { for (const delayedNode of delayedAdd) { const parenthesesNeeded = mapKey in this.parenthesesListMap && this.parenthesesListMap[mapKey].includes(delayedNode.constructor.name); if (parenthesesNeeded) { result += '('; } result += this.printNodeFormatPreserving(delayedNode, originalTokens); if (parenthesesNeeded) { result += ')'; } if (insertNewline) { result += `\n${beforeAsteriskIndent}*${afterAsteriskIndent}`; } else { result += insertStr; } } delayedAdd = []; } const parenthesesNeeded = ((_b = this.parenthesesListMap[mapKey]) === null || _b === void 0 ? void 0 : _b.includes(newNode.constructor.name)) && !((_c = this.parenthesesListMap[mapKey]) === null || _c === void 0 ? void 0 : _c.includes(originalNode.constructor.name)); const addParentheses = parenthesesNeeded && !originalTokens.hasParentheses(itemStartPos, itemEndPos); if (addParentheses) { result += '('; } result += this.printNodeFormatPreserving(newNode, originalTokens); if (addParentheses) { result += ')'; } tokenIndex = itemEndPos + 1; } else if (diffElem.type === diff_elem_1.DiffElemType.ADD) { if (insertStr === null) { return null; } if (!(newNode instanceof base_node_1.BaseNode)) { return null; } if (insertStr === ', ' && isMultiline) { insertStr = ','; insertNewline = true; } if (beforeFirstKeepOrReplace) { delayedAdd.push(newNode); continue; } const itemEndPos = tokenIndex - 1; if (insertNewline) { result += `${insertStr}\n${beforeAsteriskIndent}*${afterAsteriskIndent}`; } else { result += insertStr; } const parenthesesNeeded = (_d = this.parenthesesListMap[mapKey]) === null || _d === void 0 ? void 0 : _d.includes(newNode.constructor.name); if (parenthesesNeeded) { result += '('; } result += this.printNodeFormatPreserving(newNode, originalTokens); if (parenthesesNeeded) { result += ')'; } tokenIndex = itemEndPos + 1; } else if (diffElem.type === diff_elem_1.DiffElemType.REMOVE) { if (!(originalNode instanceof base_node_1.BaseNode)) { return null; } const itemStartPos = originalNode.getAttribute(types_1.Attribute.START_INDEX); const itemEndPos = originalNode.getAttribute(types_1.Attribute.END_INDEX); if (itemStartPos < 0 || itemEndPos < 0) { throw new Error('Invalid index'); } if (i === 0) { const originalTokensArray = originalTokens.getTokens(); for (let j = tokenIndex; j < itemStartPos; j++) { if (originalTokensArray[j][lexer_1.Lexer.TYPE_OFFSET] === lexer_1.Lexer.TOKEN_PHPDOC_EOL) { break; } result += originalTokensArray[j][lexer_1.Lexer.VALUE_OFFSET]; } } tokenIndex = itemEndPos + 1; } if (delayedAdd.length > 0) { if (!(mapKey in this.emptyListInsertionMap)) { return null; } const [findToken, extraLeft, extraRight] = this.emptyListInsertionMap[mapKey]; if (findToken !== null) { const originalTokensArray = originalTokens.getTokens(); for (; tokenIndex < originalTokensArray.length; tokenIndex++) { result += originalTokensArray[tokenIndex][lexer_1.Lexer.VALUE_OFFSET]; if (originalTokensArray[tokenIndex][lexer_1.Lexer.VALUE_OFFSET] !== findToken) { continue; } tokenIndex++; break; } } let isFirst = true; result += extraLeft; for (const delayedAddNode of delayedAdd) { if (!isFirst) { result += insertStr; if (insertNewline) { result += `${(_e = originalTokens.getDetectedNewline()) !== null && _e !== void 0 ? _e : '\n'}${beforeAsteriskIndent}${afterAsteriskIndent}`; } result += this.printNodeFormatPreserving(delayedAddNode, originalTokens); isFirst = false; } result += extraRight; } } } return [result, tokenIndex]; } isMultiline(initialIndex, nodes, originalTokens) { let isMultiline = nodes.length > 1; let pos = initialIndex; let allText = ''; for (const node of nodes) { if (!(node instanceof base_node_1.BaseNode)) { continue; } const endPos = node.getAttribute(types_1.Attribute.END_INDEX) + 1; const text = originalTokens.getContentBetween(pos, endPos); allText += text; if (text.indexOf('\n') === -1) { isMultiline = false; } pos = endPos; } const matches = allText.matchAll(/\n(\s*)\*(\s*)/g); let before = ''; let after = ''; for (const match of matches) { if (match[1].length > before.length) { before = match[1]; } if (match[2].length > after.length) { after = match[2]; } } return [isMultiline, before, after]; } printNodeFormatPreserving(node, originalTokens) { const originalNode = node.getAttribute(types_1.Attribute.ORIGINAL_NODE); if (!originalNode) { return this.print(node); } const className = node.constructor.name; if (className !== originalNode.constructor.name) { throw new Error('Class name mismatch'); } const startPos = originalNode.getAttribute(types_1.Attribute.START_INDEX); const endPos = originalNode.getAttribute(types_1.Attribute.END_INDEX); if (startPos < 0 || endPos < 0) { throw new Error('Invalid start or end index'); } let result = ''; let pos = startPos; const subNodeNames = Object.keys(node); for (const subNodeName of subNodeNames) { const subNode = node[subNodeName]; const origSubNode = originalNode[subNodeName]; if (!(subNode instanceof base_node_1.BaseNode) || !(origSubNode instanceof base_node_1.BaseNode)) { if (subNode === origSubNode) { continue; } if (Array.isArray(subNode) && Array.isArray(origSubNode)) { let listResult; [listResult, pos] = this.printArrayFormatPreserving(subNode, origSubNode, originalTokens, pos, className, subNodeName); if (listResult === null) { return this.print(node); } result += listResult; continue; } return this.print(node); } const subStartPos = origSubNode.getAttribute(types_1.Attribute.START_INDEX); const subEndPos = origSubNode.getAttribute(types_1.Attribute.END_INDEX); if (subStartPos < 0 || subEndPos < 0) { throw new Error('Invalid start or end index'); } if (!subNode) { return this.print(node); } result += originalTokens.getContentBetween(pos, subStartPos); const mapKey = `${node.constructor.name}->${subNodeName}`; let isParenthesesNeeded = mapKey in this.parenthesesMap && this.parenthesesMap[mapKey].includes(subNode.constructor.name); if (subNode.getAttribute(types_1.Attribute.ORIGINAL_NODE) !== null) { isParenthesesNeeded = isParenthesesNeeded && this.parenthesesMap[mapKey].includes(subNode.getAttribute(types_1.Attribute.ORIGINAL_NODE) .constructor.name); } const addParentheses = isParenthesesNeeded && !originalTokens.hasParentheses(subStartPos, subEndPos); if (addParentheses) { result += '('; } result += this.printNodeFormatPreserving(subNode, originalTokens); if (addParentheses) { result += ')'; } pos = subEndPos + 1; } return result + originalTokens.getContentBetween(pos, endPos + 1); } } exports.Printer = Printer;