5954 lines
204 KiB
Go
5954 lines
204 KiB
Go
// Package printer exports a Printer for pretty-printing TS ASTs and writer interfaces and implementations for using them
|
|
// Intended ultimate usage:
|
|
//
|
|
// func nodeToInlineStr(node *ast.Node) {
|
|
// // Reuse singleton single-line writer (TODO: thread safety?)
|
|
// p = printer.NewPrinter(printer.PrinterOptions{ RemoveComments: true }, printer.PrintHandlers{})
|
|
// p.Write(node, nil /*sourceFile*/, printer.SingleLineTextWriter)
|
|
// return printer.SingleLineTextWriter.getText()
|
|
// }
|
|
//
|
|
// // or
|
|
//
|
|
// func nodeToStr(node *ast.Node, options CompilerOptions) {
|
|
// // Use own writer
|
|
// p := printer.NewPrinter(printer.PrinterOptions{ NewLine: options.NewLine}, printer.PrintHandlers{})
|
|
// return p.Emit(node, nil /*sourceFile*/)
|
|
// }
|
|
package printer
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/sourcemap"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
)
|
|
|
|
type PrinterOptions struct {
|
|
RemoveComments bool
|
|
NewLine core.NewLineKind
|
|
// OmitTrailingSemicolon bool
|
|
NoEmitHelpers bool
|
|
// Module core.ModuleKind
|
|
// ModuleResolution core.ModuleResolutionKind
|
|
// Target core.ScriptTarget
|
|
SourceMap bool
|
|
InlineSourceMap bool
|
|
InlineSources bool
|
|
OmitBraceSourceMapPositions bool
|
|
// ExtendedDiagnostics bool
|
|
OnlyPrintJSDocStyle bool
|
|
NeverAsciiEscape bool
|
|
// StripInternal bool
|
|
PreserveSourceNewlines bool
|
|
TerminateUnterminatedLiterals bool // !!!
|
|
}
|
|
|
|
type PrintHandlers struct {
|
|
// A hook used by the Printer when generating unique names to avoid collisions with
|
|
// globally defined names that exist outside of the current source file.
|
|
HasGlobalName func(name string) bool
|
|
|
|
// !!!
|
|
////// A hook used by the Printer to provide notifications prior to emitting a node. A
|
|
////// compatible implementation **must** invoke `emitCallback` with the provided `hint` and
|
|
////// `node` values.
|
|
////// @param hint A hint indicating the intended purpose of the node.
|
|
////// @param node The node to emit.
|
|
////// @param emitCallback A callback that, when invoked, will emit the node.
|
|
////// @example
|
|
////// ```ts
|
|
////// var printer = createPrinter(printerOptions, {
|
|
////// onEmitNode(hint, node, emitCallback) {
|
|
////// // set up or track state prior to emitting the node...
|
|
////// emitCallback(hint, node);
|
|
////// // restore state after emitting the node...
|
|
////// }
|
|
////// });
|
|
////// ```
|
|
////OnEmitNode func(hint EmitHint, node *ast.Node, emitCallback func(hint EmitHint, node *ast.Node))
|
|
|
|
// !!!
|
|
////// A hook used to check if an emit notification is required for a node.
|
|
////// @param node The node to emit.
|
|
////IsEmitNotificationEnabled func(node *ast.Node) bool
|
|
|
|
// !!!
|
|
////// A hook used by the Printer to perform just-in-time substitution of a node. This is
|
|
////// primarily used by node transformations that need to substitute one node for another,
|
|
////// such as replacing `myExportedVar` with `exports.myExportedVar`.
|
|
////// @param hint A hint indicating the intended purpose of the node.
|
|
////// @param node The node to emit.
|
|
////// @example
|
|
////// ```ts
|
|
////// var printer = createPrinter(printerOptions, {
|
|
////// substituteNode(hint, node) {
|
|
////// // perform substitution if necessary...
|
|
////// return node;
|
|
////// }
|
|
////// });
|
|
////// ```
|
|
////SubstituteNode func(hint EmitHint, node *ast.Node) *ast.Node
|
|
|
|
// !!!
|
|
////OnEmitSourceMapOfNode func(hint EmitHint, node *ast.Node, emitCallback func(hint EmitHint, node *ast.Node))
|
|
////OnEmitSourceMapOfToken func(nodeOpt *ast.Node | undefined, token: ast.Kind, writeKind WriteKind, pos int, emitCallback func(token ast.Kind, writeKind WriteKind, pos int) int) int
|
|
////OnEmitSourceMapOfPosition func(pos int)
|
|
|
|
OnBeforeEmitNode func(nodeOpt *ast.Node)
|
|
OnAfterEmitNode func(nodeOpt *ast.Node)
|
|
OnBeforeEmitNodeList func(nodesOpt *ast.NodeList)
|
|
OnAfterEmitNodeList func(nodesOpt *ast.NodeList)
|
|
OnBeforeEmitToken func(nodeOpt *ast.TokenNode)
|
|
OnAfterEmitToken func(nodeOpt *ast.TokenNode)
|
|
}
|
|
|
|
type Printer struct {
|
|
PrintHandlers
|
|
Options PrinterOptions
|
|
emitContext *EmitContext
|
|
currentSourceFile *ast.SourceFile
|
|
uniqueHelperNames map[string]*ast.IdentifierNode
|
|
externalHelpersModuleName *ast.IdentifierNode
|
|
nextListElementPos int
|
|
writer EmitTextWriter
|
|
ownWriter EmitTextWriter
|
|
writeKind WriteKind
|
|
sourceMapsDisabled bool
|
|
sourceMapGenerator *sourcemap.Generator
|
|
sourceMapSource sourcemap.Source
|
|
sourceMapSourceIndex sourcemap.SourceIndex
|
|
sourceMapSourceIsJson bool
|
|
mostRecentSourceMapSource sourcemap.Source
|
|
mostRecentSourceMapSourceIndex sourcemap.SourceIndex
|
|
containerPos int
|
|
containerEnd int
|
|
declarationListContainerEnd int
|
|
detachedCommentsInfo core.Stack[detachedCommentsInfo]
|
|
commentsDisabled bool
|
|
inExtends bool // whether we are emitting the `extends` clause of a ConditionalType or InferType
|
|
nameGenerator NameGenerator
|
|
makeFileLevelOptimisticUniqueName func(string) string
|
|
commentStatePool core.Pool[commentState]
|
|
sourceMapStatePool core.Pool[sourceMapState]
|
|
}
|
|
|
|
type detachedCommentsInfo struct {
|
|
nodePos int
|
|
detachedCommentEndPos int
|
|
}
|
|
|
|
type commentState struct {
|
|
emitFlags EmitFlags // holds the emit flags for the current node
|
|
commentRange core.TextRange // holds the comment range calculated for the current node
|
|
containerPos int // captures the value of containerPos prior to entering an node
|
|
containerEnd int // captures the value of containerEnd prior to entering an node
|
|
declarationListContainerEnd int // captures the value of declarationListContainerEnd prior to entering an node
|
|
}
|
|
|
|
type sourceMapState struct {
|
|
emitFlags EmitFlags // holds the emit flags for the current node
|
|
sourceMapRange core.TextRange // holds the source map range calculated for the current node
|
|
hasTokenSourceMapRange bool // captures whether the source map range was set for the current node
|
|
}
|
|
|
|
type printerState struct {
|
|
commentState *commentState
|
|
sourceMapState *sourceMapState
|
|
}
|
|
|
|
func NewPrinter(options PrinterOptions, handlers PrintHandlers, emitContext *EmitContext) *Printer {
|
|
printer := &Printer{
|
|
PrintHandlers: handlers,
|
|
Options: options,
|
|
emitContext: emitContext,
|
|
}
|
|
// wire up name generator
|
|
if printer.emitContext == nil {
|
|
printer.emitContext = NewEmitContext()
|
|
}
|
|
printer.nameGenerator.Context = printer.emitContext
|
|
printer.nameGenerator.GetTextOfNode = func(node *ast.Node) string { return printer.getTextOfNode(node, false) }
|
|
printer.nameGenerator.IsFileLevelUniqueNameInCurrentFile = printer.isFileLevelUniqueNameInCurrentFile
|
|
printer.containerPos = -1
|
|
printer.containerEnd = -1
|
|
printer.declarationListContainerEnd = -1
|
|
printer.commentsDisabled = options.RemoveComments
|
|
return printer
|
|
}
|
|
|
|
func (p *Printer) getLiteralTextOfNode(node *ast.LiteralLikeNode, sourceFile *ast.SourceFile, flags getLiteralTextFlags) string {
|
|
if ast.IsStringLiteral(node) {
|
|
if textSourceNode, ok := p.emitContext.textSource[node]; ok && textSourceNode != nil {
|
|
var text string
|
|
switch textSourceNode.Kind {
|
|
default:
|
|
return p.getLiteralTextOfNode(textSourceNode, ast.GetSourceFileOfNode(textSourceNode), flags)
|
|
case ast.KindNumericLiteral:
|
|
text = textSourceNode.Text()
|
|
case ast.KindIdentifier, ast.KindPrivateIdentifier, ast.KindJsxNamespacedName:
|
|
text = p.getTextOfNode(textSourceNode, false)
|
|
}
|
|
|
|
switch {
|
|
case flags&getLiteralTextFlagsJsxAttributeEscape != 0:
|
|
return "\"" + escapeJsxAttributeString(text, QuoteCharDoubleQuote) + "\""
|
|
case flags&getLiteralTextFlagsNeverAsciiEscape != 0 || p.emitContext.EmitFlags(node)&EFNoAsciiEscaping != 0:
|
|
return "\"" + EscapeString(text, QuoteCharDoubleQuote) + "\""
|
|
default:
|
|
return "\"" + escapeNonAsciiString(text, QuoteCharDoubleQuote) + "\""
|
|
}
|
|
}
|
|
}
|
|
|
|
// !!! Printer option to control whether to terminate unterminated literals
|
|
// !!! If necessary, printer option to control whether to preserve numeric separators
|
|
if p.emitContext.EmitFlags(node)&EFNoAsciiEscaping != 0 {
|
|
flags |= getLiteralTextFlagsNeverAsciiEscape
|
|
}
|
|
return getLiteralText(node, core.Coalesce(sourceFile, p.currentSourceFile), flags)
|
|
}
|
|
|
|
// `node` must be one of Identifier | PrivateIdentifier | LiteralExpression | JsxNamespacedName
|
|
func (p *Printer) getTextOfNode(node *ast.Node, includeTrivia bool) string {
|
|
if ast.IsMemberName(node) && p.emitContext.autoGenerate[node] != nil {
|
|
return p.nameGenerator.GenerateName(node)
|
|
}
|
|
|
|
if ast.IsStringLiteral(node) {
|
|
if textSourceNode := p.emitContext.textSource[node]; textSourceNode != nil {
|
|
return p.getTextOfNode(textSourceNode, includeTrivia)
|
|
}
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindIdentifier,
|
|
ast.KindPrivateIdentifier,
|
|
ast.KindJsxNamespacedName:
|
|
// !!! If `node` is not a parse tree node, verify its original node comes from the same source file
|
|
if p.currentSourceFile == nil || node.Parent == nil || ast.NodeIsSynthesized(node) {
|
|
return node.Text()
|
|
}
|
|
case ast.KindStringLiteral,
|
|
ast.KindNumericLiteral,
|
|
ast.KindBigIntLiteral,
|
|
ast.KindNoSubstitutionTemplateLiteral,
|
|
ast.KindTemplateHead,
|
|
ast.KindTemplateMiddle,
|
|
ast.KindTemplateTail:
|
|
return p.getLiteralTextOfNode(node, nil /*sourceFile*/, getLiteralTextFlagsNone)
|
|
default:
|
|
panic(fmt.Sprintf("unexpected node: %v", node.Kind))
|
|
}
|
|
return scanner.GetSourceTextOfNodeFromSourceFile(p.currentSourceFile, node, includeTrivia)
|
|
}
|
|
|
|
//
|
|
// Low-level writing
|
|
//
|
|
|
|
type WriteKind int
|
|
|
|
const (
|
|
WriteKindNone WriteKind = iota
|
|
WriteKindKeyword
|
|
WriteKindOperator
|
|
WriteKindPunctuation
|
|
WriteKindStringLiteral
|
|
WriteKindParameter
|
|
WriteKindProperty
|
|
WriteKindComment
|
|
WriteKindLiteral
|
|
)
|
|
|
|
func (p *Printer) writeAs(text string, writeKind WriteKind) {
|
|
switch writeKind {
|
|
case WriteKindNone:
|
|
p.writer.Write(text)
|
|
case WriteKindParameter:
|
|
p.writeParameter(text)
|
|
case WriteKindKeyword:
|
|
p.writeKeyword(text)
|
|
case WriteKindOperator:
|
|
p.writeOperator(text)
|
|
case WriteKindProperty:
|
|
p.writeProperty(text)
|
|
case WriteKindPunctuation:
|
|
p.writePunctuation(text)
|
|
case WriteKindStringLiteral:
|
|
p.writer.WriteStringLiteral(text)
|
|
case WriteKindComment:
|
|
p.writeComment(text)
|
|
case WriteKindLiteral:
|
|
p.writeLiteral(text)
|
|
default:
|
|
panic(fmt.Sprintf("unexpected printer.WriteKind: %v", writeKind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) write(text string) {
|
|
p.writeAs(text, p.writeKind)
|
|
}
|
|
|
|
func (p *Printer) setWriteKind(kind WriteKind) WriteKind {
|
|
previous := p.writeKind
|
|
p.writeKind = kind
|
|
return previous
|
|
}
|
|
|
|
func (p *Printer) writeSymbol(text string, optSymbol *ast.Symbol) {
|
|
if optSymbol == nil {
|
|
p.write(text)
|
|
} else {
|
|
p.writer.WriteSymbol(text, optSymbol)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) writeLiteral(text string) {
|
|
p.writer.WriteLiteral(text)
|
|
}
|
|
|
|
func (p *Printer) writePunctuation(text string) {
|
|
p.writer.WritePunctuation(text)
|
|
}
|
|
|
|
func (p *Printer) writeOperator(text string) {
|
|
p.writer.WriteOperator(text)
|
|
}
|
|
|
|
func (p *Printer) writeKeyword(text string) {
|
|
p.writer.WriteKeyword(text)
|
|
}
|
|
|
|
func (p *Printer) writeProperty(text string) {
|
|
p.writer.WriteProperty(text)
|
|
}
|
|
|
|
func (p *Printer) writeParameter(text string) {
|
|
p.writer.WriteParameter(text)
|
|
}
|
|
|
|
func (p *Printer) writeComment(text string) {
|
|
p.writer.WriteComment(text)
|
|
}
|
|
|
|
func (p *Printer) writeSpace() {
|
|
p.writer.WriteSpace(" ")
|
|
}
|
|
|
|
func (p *Printer) writeLine() {
|
|
p.writer.WriteLine()
|
|
}
|
|
|
|
func (p *Printer) writeLineRepeat(count int) {
|
|
for range count {
|
|
p.writeLine()
|
|
}
|
|
}
|
|
|
|
func (p *Printer) writeLines(text string) {
|
|
lines := stringutil.SplitLines(text)
|
|
indentation := stringutil.GuessIndentation(lines)
|
|
for _, line := range lines {
|
|
if indentation > 0 {
|
|
line = line[indentation:]
|
|
}
|
|
if len(line) > 0 {
|
|
p.writeLine()
|
|
p.write(line)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Printer) writeTrailingSemicolon() {
|
|
p.writer.WriteTrailingSemicolon(";")
|
|
}
|
|
|
|
func (p *Printer) increaseIndent() {
|
|
p.writer.IncreaseIndent()
|
|
}
|
|
|
|
func (p *Printer) decreaseIndent() {
|
|
p.writer.DecreaseIndent()
|
|
}
|
|
|
|
func (p *Printer) increaseIndentIf(indentRequested bool) {
|
|
if indentRequested {
|
|
p.increaseIndent()
|
|
}
|
|
}
|
|
|
|
func (p *Printer) decreaseIndentIf(indentRequested bool) {
|
|
if indentRequested {
|
|
p.decreaseIndent()
|
|
}
|
|
}
|
|
|
|
func (p *Printer) writeLineOrSpace(parentNode *ast.Node, prevChildNode *ast.Node, nextChildNode *ast.Node) {
|
|
if p.shouldEmitOnSingleLine(parentNode) {
|
|
p.writeSpace()
|
|
} else if p.Options.PreserveSourceNewlines {
|
|
lines := p.getLinesBetweenNodes(parentNode, prevChildNode, nextChildNode)
|
|
if lines > 0 {
|
|
p.writeLineRepeat(lines)
|
|
} else {
|
|
p.writeSpace()
|
|
}
|
|
} else {
|
|
p.writeLine()
|
|
}
|
|
}
|
|
|
|
func (p *Printer) writeLinesAndIndent(lineCount int, writeSpaceIfNotIndenting bool) {
|
|
if lineCount > 0 {
|
|
p.increaseIndent()
|
|
p.writeLineRepeat(lineCount)
|
|
} else if writeSpaceIfNotIndenting {
|
|
p.writeSpace()
|
|
}
|
|
}
|
|
|
|
func (p *Printer) writeLineSeparatorsAndIndentBefore(node *ast.Node, parent *ast.Node) bool {
|
|
if p.Options.PreserveSourceNewlines {
|
|
leadingNewlines := p.getLeadingLineTerminatorCount(parent, node, LFNone)
|
|
if leadingNewlines > 0 {
|
|
p.writeLinesAndIndent(leadingNewlines /*writeSpaceIfNotIndenting*/, false)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (p *Printer) writeLineSeparatorsAfter(node *ast.Node, parent *ast.Node) {
|
|
if p.Options.PreserveSourceNewlines {
|
|
trailingNewlines := p.getClosingLineTerminatorCount(parent, node, LFNone, core.NewTextRange(-1, -1) /*childrenTextRange*/)
|
|
if trailingNewlines > 0 {
|
|
p.writeLineRepeat(trailingNewlines)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Printer) getLinesBetweenNodes(parent *ast.Node, node1 *ast.Node, node2 *ast.Node) int {
|
|
if p.shouldElideIndentation(parent) {
|
|
return 0
|
|
}
|
|
|
|
parent = skipSynthesizedParentheses(parent)
|
|
node1 = skipSynthesizedParentheses(node1)
|
|
node2 = skipSynthesizedParentheses(node2)
|
|
|
|
// Always use a newline for synthesized code if the synthesizer desires it.
|
|
if p.shouldEmitOnNewLine(node2, LFNone) {
|
|
return 1
|
|
}
|
|
|
|
if p.currentSourceFile != nil && !ast.NodeIsSynthesized(parent) && !ast.NodeIsSynthesized(node1) && !ast.NodeIsSynthesized(node2) {
|
|
if p.Options.PreserveSourceNewlines {
|
|
return p.getEffectiveLines(
|
|
func(includeComments bool) int {
|
|
return getLinesBetweenRangeEndAndRangeStart(
|
|
node1.Loc,
|
|
node2.Loc,
|
|
p.currentSourceFile,
|
|
includeComments,
|
|
)
|
|
},
|
|
)
|
|
}
|
|
return core.IfElse(rangeEndIsOnSameLineAsRangeStart(node1.Loc, node2.Loc, p.currentSourceFile), 0, 1)
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func (p *Printer) getEffectiveLines(getLineDifference func(includeComments bool) int) int {
|
|
// If 'preserveSourceNewlines' is disabled, we should never call this function
|
|
// because it could be more expensive than alternative approximations.
|
|
if !p.Options.PreserveSourceNewlines {
|
|
panic("Should not be called when preserveSourceNewlines is false")
|
|
}
|
|
// We start by measuring the line difference from a position to its adjacent comments,
|
|
// so that this is counted as a one-line difference, not two:
|
|
//
|
|
// node1;
|
|
// // NODE2 COMMENT
|
|
// node2;
|
|
lines := getLineDifference( /*includeComments*/ true)
|
|
if lines == 0 {
|
|
// However, if the line difference considering comments was 0, we might have this:
|
|
//
|
|
// node1; // NODE2 COMMENT
|
|
// node2;
|
|
//
|
|
// in which case we should be ignoring node2's comment, so this too is counted as
|
|
// a one-line difference, not zero.
|
|
return getLineDifference( /*includeComments*/ false)
|
|
}
|
|
return lines
|
|
}
|
|
|
|
func (p *Printer) getLeadingLineTerminatorCount(parentNode *ast.Node, firstChild *ast.Node, format ListFormat) int {
|
|
if format&LFPreserveLines != 0 || p.Options.PreserveSourceNewlines {
|
|
if format&LFPreferNewLine != 0 {
|
|
return 1
|
|
}
|
|
|
|
if firstChild == nil {
|
|
return core.IfElse(parentNode == nil || p.currentSourceFile != nil && rangeIsOnSingleLine(parentNode.Loc, p.currentSourceFile), 0, 1)
|
|
}
|
|
if p.nextListElementPos > 0 && firstChild.Pos() == p.nextListElementPos {
|
|
// If this child starts at the beginning of a list item in a parent list, its leading
|
|
// line terminators have already been written as the separating line terminators of the
|
|
// parent list. Example:
|
|
//
|
|
// class Foo {
|
|
// constructor() {}
|
|
// public foo() {}
|
|
// }
|
|
//
|
|
// The outer list is the list of class members, with one line terminator between the
|
|
// constructor and the method. The constructor is written, the separating line terminator
|
|
// is written, and then we start emitting the method. Its modifiers ([public]) constitute an inner
|
|
// list, so we look for its leading line terminators. If we didn't know that we had already
|
|
// written a newline as part of the parent list, it would appear that we need to write a
|
|
// leading newline to start the modifiers.
|
|
return 0
|
|
}
|
|
if firstChild.Kind == ast.KindJsxText {
|
|
// JsxText will be written with its leading whitespace, so don't add more manually.
|
|
return 0
|
|
}
|
|
if p.currentSourceFile != nil && parentNode != nil &&
|
|
!ast.PositionIsSynthesized(parentNode.Pos()) &&
|
|
!ast.NodeIsSynthesized(firstChild) &&
|
|
(firstChild.Parent == nil /*|| getOriginalNode(firstChild.Parent) == getOriginalNode(parentNode)*/) {
|
|
if p.Options.PreserveSourceNewlines {
|
|
return p.getEffectiveLines(
|
|
func(includeComments bool) int {
|
|
return getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter(
|
|
firstChild.Pos(),
|
|
parentNode.Pos(),
|
|
p.currentSourceFile,
|
|
includeComments,
|
|
)
|
|
},
|
|
)
|
|
}
|
|
return core.IfElse(rangeStartPositionsAreOnSameLine(parentNode.Loc, firstChild.Loc, p.currentSourceFile), 0, 1)
|
|
}
|
|
if p.shouldEmitOnNewLine(firstChild, format) {
|
|
return 1
|
|
}
|
|
}
|
|
return core.IfElse(format&LFMultiLine != 0, 1, 0)
|
|
}
|
|
|
|
func (p *Printer) getSeparatingLineTerminatorCount(previousNode *ast.Node, nextNode *ast.Node, format ListFormat) int {
|
|
if format&LFPreserveLines != 0 || p.Options.PreserveSourceNewlines {
|
|
if previousNode == nil || nextNode == nil {
|
|
return 0
|
|
}
|
|
if nextNode.Kind == ast.KindJsxText {
|
|
// JsxText will be written with its leading whitespace, so don't add more manually.
|
|
return 0
|
|
} else if p.currentSourceFile != nil && !ast.NodeIsSynthesized(previousNode) && !ast.NodeIsSynthesized(nextNode) {
|
|
if p.Options.PreserveSourceNewlines && siblingNodePositionsAreComparable(previousNode, nextNode) {
|
|
return p.getEffectiveLines(
|
|
func(includeComments bool) int {
|
|
return getLinesBetweenRangeEndAndRangeStart(
|
|
previousNode.Loc,
|
|
nextNode.Loc,
|
|
p.currentSourceFile,
|
|
includeComments,
|
|
)
|
|
},
|
|
)
|
|
} else if !p.Options.PreserveSourceNewlines && originalNodesHaveSameParent(previousNode, nextNode) {
|
|
// If `preserveSourceNewlines` is `false` we do not intend to preserve the effective lines between the
|
|
// previous and next node. Instead we naively check whether nodes are on separate lines within the
|
|
// same node parent. If so, we intend to preserve a single line terminator. This is less precise and
|
|
// expensive than checking with `preserveSourceNewlines` as above, but the goal is not to preserve the
|
|
// effective source lines between two sibling nodes.
|
|
return core.IfElse(rangeEndIsOnSameLineAsRangeStart(previousNode.Loc, nextNode.Loc, p.currentSourceFile), 0, 1)
|
|
}
|
|
// If the two nodes are not comparable, add a line terminator based on the format that can indicate
|
|
// whether new lines are preferred or not.
|
|
return core.IfElse(format&LFPreferNewLine != 0, 1, 0)
|
|
} else if p.shouldEmitOnNewLine(previousNode, format) || p.shouldEmitOnNewLine(nextNode, format) {
|
|
return 1
|
|
}
|
|
} else if p.shouldEmitOnNewLine(nextNode, LFNone) {
|
|
return 1
|
|
}
|
|
return core.IfElse(format&LFMultiLine != 0, 1, 0)
|
|
}
|
|
|
|
func (p *Printer) getClosingLineTerminatorCount(parentNode *ast.Node, lastChild *ast.Node, format ListFormat, childrenTextRange core.TextRange) int {
|
|
if format&LFPreserveLines != 0 || p.Options.PreserveSourceNewlines {
|
|
if format&LFPreferNewLine != 0 {
|
|
return 1
|
|
}
|
|
if lastChild == nil {
|
|
return core.IfElse(parentNode == nil || p.currentSourceFile != nil && rangeIsOnSingleLine(parentNode.Loc, p.currentSourceFile), 0, 1)
|
|
}
|
|
if p.currentSourceFile != nil && parentNode != nil && !ast.PositionIsSynthesized(parentNode.Pos()) && !ast.NodeIsSynthesized(lastChild) && (lastChild.Parent == nil || lastChild.Parent == parentNode) {
|
|
if p.Options.PreserveSourceNewlines {
|
|
end := greatestEnd(lastChild.End(), childrenTextRange)
|
|
return p.getEffectiveLines(
|
|
func(includeComments bool) int {
|
|
return getLinesBetweenPositionAndNextNonWhitespaceCharacter(
|
|
end,
|
|
parentNode.End(),
|
|
p.currentSourceFile,
|
|
includeComments,
|
|
)
|
|
},
|
|
)
|
|
}
|
|
return core.IfElse(rangeEndPositionsAreOnSameLine(parentNode.Loc, lastChild.Loc, p.currentSourceFile), 0, 1)
|
|
}
|
|
if p.shouldEmitOnNewLine(lastChild, format) {
|
|
return 1
|
|
}
|
|
}
|
|
if format&LFMultiLine != 0 && format&LFNoTrailingNewLine == 0 {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (p *Printer) writeCommentRange(comment ast.CommentRange) {
|
|
if p.currentSourceFile == nil {
|
|
return
|
|
}
|
|
|
|
text := p.currentSourceFile.Text()
|
|
lineMap := p.currentSourceFile.ECMALineMap()
|
|
p.writeCommentRangeWorker(text, lineMap, comment.Kind, comment.TextRange)
|
|
}
|
|
|
|
func (p *Printer) writeCommentRangeWorker(text string, lineMap []core.TextPos, kind ast.Kind, loc core.TextRange) {
|
|
if kind == ast.KindMultiLineCommentTrivia {
|
|
indentSize := len(getIndentString(1))
|
|
firstLine := scanner.ComputeLineOfPosition(lineMap, loc.Pos())
|
|
lineCount := len(lineMap)
|
|
firstCommentLineIndent := -1
|
|
pos := loc.Pos()
|
|
currentLine := firstLine
|
|
for ; pos < loc.End(); currentLine++ {
|
|
var nextLineStart int
|
|
if currentLine+1 == lineCount {
|
|
nextLineStart = len(text) + 1
|
|
} else {
|
|
nextLineStart = int(lineMap[currentLine+1])
|
|
}
|
|
|
|
if pos != loc.Pos() {
|
|
// If we are not emitting first line, we need to write the spaces to adjust the alignment
|
|
if firstCommentLineIndent == -1 {
|
|
firstCommentLineIndent = calculateIndent(text, int(lineMap[firstLine]), loc.Pos())
|
|
}
|
|
|
|
// These are number of spaces writer is going to write at current indent
|
|
currentWriterIndentSpacing := p.writer.GetIndent() * indentSize
|
|
|
|
// Number of spaces we want to be writing
|
|
// eg: Assume writer indent
|
|
// module m {
|
|
// /* starts at character 9 this is line 1
|
|
// * starts at character pos 4 line --1 = 8 - 8 + 3
|
|
// More left indented comment */ --2 = 8 - 8 + 2
|
|
// class c { }
|
|
// }
|
|
// module m {
|
|
// /* this is line 1 -- Assume current writer indent 8
|
|
// * line --3 = 8 - 4 + 5
|
|
// More right indented comment */ --4 = 8 - 4 + 11
|
|
// class c { }
|
|
// }
|
|
spacesToEmit := currentWriterIndentSpacing - firstCommentLineIndent + calculateIndent(text, pos, nextLineStart)
|
|
if spacesToEmit > 0 {
|
|
numberOfSingleSpacesToEmit := spacesToEmit % indentSize
|
|
indentSizeSpaceString := getIndentString((spacesToEmit - numberOfSingleSpacesToEmit) / indentSize)
|
|
|
|
// Write indent size string ( in eg 1: = "", 2: "" , 3: string with 8 spaces 4: string with 12 spaces
|
|
p.writer.RawWrite(indentSizeSpaceString)
|
|
|
|
// Emit the single spaces (in eg: 1: 3 spaces, 2: 2 spaces, 3: 1 space, 4: 3 spaces)
|
|
for numberOfSingleSpacesToEmit > 0 {
|
|
p.writer.RawWrite(" ")
|
|
numberOfSingleSpacesToEmit--
|
|
}
|
|
} else {
|
|
// No spaces to emit write empty string
|
|
p.writer.RawWrite("")
|
|
}
|
|
}
|
|
|
|
// Write the comment line text
|
|
end := min(loc.End(), nextLineStart-1)
|
|
currentLineText := strings.TrimSpace(text[pos:end])
|
|
if len(currentLineText) > 0 {
|
|
p.writeComment(currentLineText)
|
|
if end != loc.End() {
|
|
p.writeLine()
|
|
}
|
|
} else {
|
|
// Empty string - make sure we write empty line
|
|
p.writer.WriteLineForce(true)
|
|
}
|
|
|
|
pos = nextLineStart
|
|
}
|
|
} else {
|
|
// Single line comment of style //....
|
|
p.writeComment(text[loc.Pos():loc.End()])
|
|
}
|
|
}
|
|
|
|
//
|
|
// Custom emit behavior stubs (i.e., from `EmitNode`, `EmitFlags`, etc.)
|
|
//
|
|
|
|
func (p *Printer) shouldEmitComments(node *ast.Node) bool {
|
|
return !p.commentsDisabled &&
|
|
p.currentSourceFile != nil &&
|
|
!ast.IsSourceFile(node)
|
|
}
|
|
|
|
func (p *Printer) shouldWriteComment(comment ast.CommentRange) bool {
|
|
return !p.Options.OnlyPrintJSDocStyle ||
|
|
p.currentSourceFile != nil && isJSDocLikeText(p.currentSourceFile.Text(), comment) ||
|
|
p.currentSourceFile != nil && IsPinnedComment(p.currentSourceFile.Text(), comment)
|
|
}
|
|
|
|
func (p *Printer) shouldEmitIndented(node *ast.Node) bool {
|
|
return p.emitContext.EmitFlags(node)&EFIndented != 0
|
|
}
|
|
|
|
func (p *Printer) shouldElideIndentation(node *ast.Node) bool {
|
|
return p.emitContext.EmitFlags(node)&EFNoIndentation != 0
|
|
}
|
|
|
|
func (p *Printer) shouldEmitOnSingleLine(node *ast.Node) bool {
|
|
return p.emitContext.EmitFlags(node)&EFSingleLine != 0
|
|
}
|
|
|
|
func (p *Printer) shouldEmitOnMultipleLines(node *ast.Node) bool {
|
|
return p.emitContext.EmitFlags(node)&EFMultiLine != 0
|
|
}
|
|
|
|
func (p *Printer) shouldEmitBlockFunctionBodyOnSingleLine(body *ast.Block) bool {
|
|
// We must emit a function body as a single-line body in the following case:
|
|
// * The body has NodeEmitFlags.SingleLine specified.
|
|
|
|
// We must emit a function body as a multi-line body in the following cases:
|
|
// * The body is explicitly marked as multi-line.
|
|
// * A non-synthesized body's start and end position are on different lines.
|
|
// * Any statement in the body starts on a new line.
|
|
|
|
if p.shouldEmitOnSingleLine(body.AsNode()) {
|
|
return true
|
|
}
|
|
|
|
if body.Multiline {
|
|
return false
|
|
}
|
|
|
|
if !ast.NodeIsSynthesized(body.AsNode()) && p.currentSourceFile != nil && !rangeIsOnSingleLine(body.Loc, p.currentSourceFile) {
|
|
return false
|
|
}
|
|
|
|
if p.getLeadingLineTerminatorCount(body.AsNode(), core.FirstOrNil(body.Statements.Nodes), LFPreserveLines) > 0 ||
|
|
p.getClosingLineTerminatorCount(body.AsNode(), core.LastOrNil(body.Statements.Nodes), LFPreserveLines, body.Statements.Loc) > 0 {
|
|
return false
|
|
}
|
|
|
|
var previousStatement *ast.Statement
|
|
for _, statement := range body.Statements.Nodes {
|
|
if p.getSeparatingLineTerminatorCount(previousStatement, statement, LFPreserveLines) > 0 {
|
|
return false
|
|
}
|
|
|
|
previousStatement = statement
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *Printer) shouldEmitOnNewLine(node *ast.Node, format ListFormat) bool {
|
|
// !!! TODO: enable multiline emit
|
|
// if p.emitContext.EmitFlags(node)&EFStartOnNewLine != 0 {
|
|
// return true
|
|
// }
|
|
return format&LFPreferNewLine != 0
|
|
}
|
|
|
|
func (p *Printer) shouldEmitSourceMaps(node *ast.Node) bool {
|
|
return !p.sourceMapsDisabled &&
|
|
p.sourceMapSource != nil &&
|
|
!ast.IsSourceFile(node) &&
|
|
!ast.IsInJsonFile(node)
|
|
}
|
|
|
|
func (p *Printer) shouldEmitTokenSourceMaps(token ast.Kind, pos int, contextNode *ast.Node, flags tokenEmitFlags) bool {
|
|
// We don't emit source positions for most tokens as it tends to be quite noisy, however
|
|
// we need to emit source positions for open and close braces so that tools like istanbul
|
|
// can map branches for code coverage. However, we still omit brace source positions when
|
|
// the output is a declaration file.
|
|
return flags&tefNoSourceMaps == 0 &&
|
|
p.shouldEmitSourceMaps(contextNode) &&
|
|
!p.Options.OmitBraceSourceMapPositions && (token == ast.KindOpenBraceToken || token == ast.KindCloseBraceToken)
|
|
}
|
|
|
|
func (p *Printer) shouldEmitLeadingComments(node *ast.Node) bool {
|
|
return p.emitContext.EmitFlags(node)&EFNoLeadingComments == 0
|
|
}
|
|
|
|
func (p *Printer) shouldEmitTrailingComments(node *ast.Node) bool {
|
|
return p.emitContext.EmitFlags(node)&EFNoTrailingComments == 0
|
|
}
|
|
|
|
func (p *Printer) shouldEmitNestedComments(node *ast.Node) bool {
|
|
return p.emitContext.EmitFlags(node)&EFNoNestedComments == 0
|
|
}
|
|
|
|
func (p *Printer) shouldEmitDetachedComments(node *ast.Node) bool {
|
|
if !ast.IsSourceFile(node) {
|
|
return true
|
|
}
|
|
|
|
file := node.AsSourceFile()
|
|
|
|
// Emit detached comment if there are no prologue directives or if the first node is synthesized.
|
|
// The synthesized node will have no leading comment so some comments may be missed.
|
|
return len(file.Statements.Nodes) == 0 ||
|
|
!ast.IsPrologueDirective(file.Statements.Nodes[0]) ||
|
|
ast.NodeIsSynthesized(file.Statements.Nodes[0])
|
|
}
|
|
|
|
func (p *Printer) hasCommentsAtPosition(pos int) bool {
|
|
// !!!
|
|
return false
|
|
}
|
|
|
|
func (p *Printer) shouldEmitIndirectCall(node *ast.Node) bool {
|
|
return p.emitContext.EmitFlags(node)&EFIndirectCall != 0
|
|
}
|
|
|
|
func (p *Printer) shouldAllowTrailingComma(node *ast.Node, list *ast.NodeList) bool {
|
|
if p.currentSourceFile == nil || p.currentSourceFile.ScriptKind == core.ScriptKindJSON {
|
|
return false
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindObjectLiteralExpression:
|
|
return true
|
|
case ast.KindArrayLiteralExpression,
|
|
ast.KindArrowFunction,
|
|
ast.KindConstructor,
|
|
ast.KindGetAccessor,
|
|
ast.KindSetAccessor,
|
|
ast.KindTypeAliasDeclaration,
|
|
ast.KindJSTypeAliasDeclaration,
|
|
ast.KindFunctionType,
|
|
ast.KindConstructorType,
|
|
ast.KindCallSignature,
|
|
ast.KindConstructSignature,
|
|
ast.KindTaggedTemplateExpression,
|
|
ast.KindObjectBindingPattern,
|
|
ast.KindArrayBindingPattern,
|
|
ast.KindNamedImports,
|
|
ast.KindNamedExports,
|
|
ast.KindImportAttributes:
|
|
return true
|
|
case ast.KindClassExpression,
|
|
ast.KindClassDeclaration:
|
|
return list == node.ClassLikeData().TypeParameters
|
|
case ast.KindInterfaceDeclaration:
|
|
return list == node.AsInterfaceDeclaration().TypeParameters
|
|
case ast.KindFunctionDeclaration,
|
|
ast.KindFunctionExpression,
|
|
ast.KindMethodDeclaration:
|
|
return true
|
|
case ast.KindCallExpression:
|
|
return true
|
|
case ast.KindNewExpression:
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
//
|
|
// Tokens/Keywords
|
|
//
|
|
|
|
func (p *Printer) writeTokenText(token ast.Kind, writeKind WriteKind, pos int) int {
|
|
// !!! emit leading and trailing comments
|
|
// !!! emit leading and trailing source maps
|
|
tokenString := scanner.TokenToString(token)
|
|
p.writeAs(tokenString, writeKind)
|
|
if ast.PositionIsSynthesized(pos) {
|
|
return pos
|
|
} else {
|
|
return pos + len(tokenString)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitToken(token ast.Kind, pos int, writeKind WriteKind, contextNode *ast.Node) int {
|
|
return p.emitTokenEx(token, pos, writeKind, contextNode, tefNone)
|
|
}
|
|
|
|
func (p *Printer) emitTokenEx(token ast.Kind, pos int, writeKind WriteKind, contextNode *ast.Node, flags tokenEmitFlags) int {
|
|
state, pos := p.enterToken(token, pos, contextNode, flags)
|
|
pos = p.writeTokenText(token, writeKind, pos)
|
|
p.exitToken(token, pos, contextNode, state)
|
|
return pos
|
|
}
|
|
|
|
func (p *Printer) emitKeywordNode(node *ast.TokenNode) {
|
|
p.emitKeywordNodeEx(node, tefNone)
|
|
}
|
|
|
|
func (p *Printer) emitKeywordNodeEx(node *ast.TokenNode, flags tokenEmitFlags) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
state := p.enterTokenNode(node, flags)
|
|
p.writeTokenText(node.Kind, WriteKindKeyword, node.Pos())
|
|
p.exitTokenNode(node, state)
|
|
}
|
|
|
|
func (p *Printer) emitPunctuationNode(node *ast.TokenNode) {
|
|
p.emitPunctuationNodeEx(node, tefNone)
|
|
}
|
|
|
|
func (p *Printer) emitPunctuationNodeEx(node *ast.TokenNode, flags tokenEmitFlags) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
state := p.enterTokenNode(node, flags)
|
|
p.writeTokenText(node.Kind, WriteKindPunctuation, node.Pos())
|
|
p.exitTokenNode(node, state)
|
|
}
|
|
|
|
func (p *Printer) emitTokenNode(node *ast.TokenNode) {
|
|
p.emitTokenNodeEx(node, tefNone)
|
|
}
|
|
|
|
func (p *Printer) emitTokenNodeEx(node *ast.TokenNode, flags tokenEmitFlags) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
switch {
|
|
case ast.IsKeywordKind(node.Kind):
|
|
p.emitKeywordNodeEx(node, flags)
|
|
case ast.IsPunctuationKind(node.Kind):
|
|
p.emitPunctuationNodeEx(node, flags)
|
|
default:
|
|
panic(fmt.Sprintf("unexpected TokenNode: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
//
|
|
// Literals
|
|
//
|
|
|
|
// Emits literals of the following kinds
|
|
//
|
|
// SyntaxKindNumericLiteral
|
|
// SyntaxKindBigIntLiteral
|
|
// SyntaxKindStringLiteral
|
|
// SyntaxKindNoSubstitutionTemplateLiteral
|
|
// SyntaxKindRegularExpressionLiteral
|
|
// SyntaxKindTemplateHead
|
|
// SyntaxKindTemplateMiddle
|
|
// SyntaxKindTemplateTail
|
|
func (p *Printer) emitLiteral(node *ast.LiteralLikeNode, flags getLiteralTextFlags) {
|
|
// Add NeverAsciiEscape flag if the printer option is set
|
|
if p.Options.NeverAsciiEscape {
|
|
flags |= getLiteralTextFlagsNeverAsciiEscape
|
|
}
|
|
|
|
text := p.getLiteralTextOfNode(node, nil /*sourceFile*/, flags)
|
|
|
|
// !!! Printer option to control source map emit, which causes us to use a different write method on the
|
|
// emit text writer:
|
|
|
|
////if (
|
|
//// (printerOptions.sourceMap || printerOptions.inlineSourceMap)
|
|
//// && (node.kind === SyntaxKindStringLiteral || isTemplateLiteralKind(node.kind))
|
|
////) {
|
|
//// writeLiteral(text);
|
|
////} else {
|
|
|
|
// Quick info expects all literals to be called with writeStringLiteral, as there's no specific type for
|
|
// numberLiterals
|
|
p.writer.WriteStringLiteral(text)
|
|
|
|
// }
|
|
}
|
|
|
|
func (p *Printer) emitNumericLiteral(node *ast.NumericLiteral) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitLiteral(node.AsNode(), getLiteralTextFlagsAllowNumericSeparator)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitBigIntLiteral(node *ast.BigIntLiteral) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone) // TODO: Preserve numeric literal separators after Strada migration
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitStringLiteral(node *ast.StringLiteral) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNoSubstitutionTemplateLiteral(node *ast.NoSubstitutionTemplateLiteral) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitRegularExpressionLiteral(node *ast.RegularExpressionLiteral) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
//
|
|
// Pseudo-literals
|
|
//
|
|
|
|
func (p *Printer) emitTemplateHead(node *ast.TemplateHead) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTemplateMiddle(node *ast.TemplateMiddle) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTemplateTail(node *ast.TemplateTail) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitLiteral(node.AsNode(), getLiteralTextFlagsNone)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTemplateMiddleTail(node *ast.TemplateMiddleOrTail) {
|
|
switch node.Kind {
|
|
case ast.KindTemplateMiddle:
|
|
p.emitTemplateMiddle(node.AsTemplateMiddle())
|
|
case ast.KindTemplateTail:
|
|
p.emitTemplateTail(node.AsTemplateTail())
|
|
}
|
|
}
|
|
|
|
//
|
|
// Snippet Elements
|
|
//
|
|
|
|
// !!! Snippet elements
|
|
|
|
//
|
|
// Names
|
|
//
|
|
|
|
func (p *Printer) emitIdentifierText(node *ast.Identifier) {
|
|
text := p.getTextOfNode(node.AsNode(), false /*includeTrivia*/)
|
|
|
|
// !!! In the old emitter, an Identifier could have a Symbol associated with it. That
|
|
// doesn't seem to be the case in the new emitter. Do we need to get the symbol from somewhere else?
|
|
////p.writeSymbol(text, node.Symbol())
|
|
p.write(text)
|
|
|
|
// !!! In the old emitter, an Identifier could have type arguments for use with quickinfo:
|
|
////p.emitList(node, getIdentifierTypeArguments(node), LFTypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
|
|
}
|
|
|
|
func (p *Printer) emitIdentifierName(node *ast.Identifier) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitIdentifierText(node)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitIdentifierNameNode(node *ast.IdentifierNode) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
p.emitIdentifierName(node.AsIdentifier())
|
|
}
|
|
|
|
func (p *Printer) getUniqueHelperName(name string) *ast.IdentifierNode {
|
|
helperName := p.uniqueHelperNames[name]
|
|
if helperName == nil {
|
|
helperName := p.emitContext.Factory.NewUniqueNameEx(name, AutoGenerateOptions{Flags: GeneratedIdentifierFlagsFileLevel | GeneratedIdentifierFlagsOptimistic})
|
|
p.generateName(helperName)
|
|
p.uniqueHelperNames[name] = helperName
|
|
return helperName
|
|
}
|
|
return helperName.Clone(p.emitContext.Factory)
|
|
}
|
|
|
|
func (p *Printer) emitIdentifierReference(node *ast.Identifier) {
|
|
if (p.externalHelpersModuleName != nil || p.uniqueHelperNames != nil) &&
|
|
p.emitContext.EmitFlags(node.AsNode())&EFHelperName != 0 {
|
|
if p.externalHelpersModuleName != nil {
|
|
// Substitute `__helper` with `tslib_1.__helper`
|
|
helper := p.emitContext.Factory.NewPropertyAccessExpression(
|
|
p.externalHelpersModuleName.Clone(p.emitContext.Factory),
|
|
nil, /*questionDotToken*/
|
|
node.Clone(p.emitContext.Factory),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
p.emitContext.AssignCommentAndSourceMapRanges(helper, node.AsNode())
|
|
p.emitPropertyAccessExpression(helper.AsPropertyAccessExpression())
|
|
return
|
|
}
|
|
if p.uniqueHelperNames != nil {
|
|
// Substitute `__helper` with `__helper_1` if there is a conflict in an ES module.
|
|
helperName := p.getUniqueHelperName(node.Text)
|
|
p.emitContext.AssignCommentAndSourceMapRanges(helperName, node.AsNode())
|
|
node = helperName.AsIdentifier()
|
|
}
|
|
}
|
|
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitIdentifierText(node)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitBindingIdentifier(node *ast.Identifier) {
|
|
if p.uniqueHelperNames != nil &&
|
|
p.emitContext.EmitFlags(node.AsNode())&EFHelperName != 0 {
|
|
// Substitute `__helper` with `__helper_1` if there is a conflict in an ES module.
|
|
helperName := p.getUniqueHelperName(node.Text)
|
|
p.emitContext.AssignCommentAndSourceMapRanges(helperName, node.AsNode())
|
|
node = helperName.AsIdentifier()
|
|
}
|
|
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitIdentifierText(node)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitLabelIdentifier(node *ast.Identifier) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitIdentifierText(node)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitPrivateIdentifier(node *ast.PrivateIdentifier) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.write(p.getTextOfNode(node.AsNode(), false /*includeTrivia*/))
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitQualifiedName(node *ast.QualifiedName) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitEntityName(node.Left)
|
|
p.writePunctuation(".")
|
|
p.emitIdentifierName(node.Right.AsIdentifier())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitComputedPropertyName(node *ast.ComputedPropertyName) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("[")
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
|
|
p.writePunctuation("]")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitEntityName(node *ast.EntityName) {
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierReference(node.AsIdentifier())
|
|
case ast.KindQualifiedName:
|
|
p.emitQualifiedName(node.AsQualifiedName())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected EntityName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitBindingName(node *ast.BindingName) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitBindingIdentifier(node.AsIdentifier())
|
|
case ast.KindObjectBindingPattern:
|
|
p.emitObjectBindingPattern(node.AsBindingPattern())
|
|
case ast.KindArrayBindingPattern:
|
|
p.emitArrayBindingPattern(node.AsBindingPattern())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected BindingName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitPropertyName(node *ast.PropertyName) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
savedWriteKind := p.writeKind
|
|
p.writeKind = WriteKindProperty
|
|
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierName(node.AsIdentifier())
|
|
case ast.KindPrivateIdentifier:
|
|
p.emitPrivateIdentifier(node.AsPrivateIdentifier())
|
|
case ast.KindStringLiteral:
|
|
p.emitStringLiteral(node.AsStringLiteral())
|
|
case ast.KindNoSubstitutionTemplateLiteral:
|
|
p.emitNoSubstitutionTemplateLiteral(node.AsNoSubstitutionTemplateLiteral())
|
|
case ast.KindNumericLiteral:
|
|
p.emitNumericLiteral(node.AsNumericLiteral())
|
|
case ast.KindBigIntLiteral:
|
|
p.emitBigIntLiteral(node.AsBigIntLiteral())
|
|
case ast.KindComputedPropertyName:
|
|
p.emitComputedPropertyName(node.AsComputedPropertyName())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected PropertyName: %v", node.Kind))
|
|
}
|
|
|
|
p.writeKind = savedWriteKind
|
|
}
|
|
|
|
func (p *Printer) emitMemberName(node *ast.MemberName) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierName(node.AsIdentifier())
|
|
case ast.KindPrivateIdentifier:
|
|
p.emitPrivateIdentifier(node.AsPrivateIdentifier())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected MemberName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitModuleName(node *ast.ModuleName) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitBindingIdentifier(node.AsIdentifier())
|
|
case ast.KindStringLiteral:
|
|
p.emitStringLiteral(node.AsStringLiteral())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected ModuleName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitModuleExportName(node *ast.ModuleExportName) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierName(node.AsIdentifier())
|
|
case ast.KindStringLiteral:
|
|
p.emitStringLiteral(node.AsStringLiteral())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected ModuleExportName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitImportAttributeName(node *ast.ImportAttributeName) {
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierName(node.AsIdentifier())
|
|
case ast.KindStringLiteral:
|
|
p.emitStringLiteral(node.AsStringLiteral())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected ImportAttributeName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitNestedModuleName(node *ast.ModuleName) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierName(node.AsIdentifier())
|
|
case ast.KindStringLiteral:
|
|
p.emitStringLiteral(node.AsStringLiteral())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected ModuleName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
//
|
|
// Signature elements
|
|
//
|
|
|
|
func (p *Printer) emitModifierList(parentNode *ast.Node, modifiers *ast.ModifierList, allowDecorators bool) int {
|
|
if modifiers == nil || len(modifiers.Nodes) == 0 {
|
|
return parentNode.Pos()
|
|
}
|
|
|
|
if core.Every(modifiers.Nodes, ast.IsModifier) {
|
|
// if all modifier-likes are `Modifier`, simply emit the list as modifiers.
|
|
p.emitList((*Printer).emitKeywordNode, parentNode, &modifiers.NodeList, LFModifiers)
|
|
} else if core.Every(modifiers.Nodes, ast.IsDecorator) {
|
|
if !allowDecorators {
|
|
return parentNode.Pos()
|
|
}
|
|
|
|
// if all modifier-likes are `Decorator`, simply emit the list as decorators.
|
|
p.emitList((*Printer).emitModifierLike, parentNode, &modifiers.NodeList, LFDecorators)
|
|
} else {
|
|
if p.OnBeforeEmitNodeList != nil {
|
|
p.OnBeforeEmitNodeList(&modifiers.NodeList)
|
|
}
|
|
|
|
// partition modifiers into contiguous chunks of `Modifier` or `Decorator` so as to
|
|
// use consistent formatting for each chunk
|
|
type Mode int
|
|
const (
|
|
ModeNone Mode = iota
|
|
ModeModifiers
|
|
ModeDecorators
|
|
)
|
|
|
|
lastMode := ModeNone
|
|
mode := ModeNone
|
|
start := 0
|
|
pos := 0
|
|
|
|
var lastModifier *ast.ModifierLike
|
|
for start < len(modifiers.Nodes) {
|
|
for pos < len(modifiers.Nodes) {
|
|
lastModifier = modifiers.Nodes[pos]
|
|
if ast.IsDecorator(lastModifier) {
|
|
mode = ModeDecorators
|
|
} else {
|
|
mode = ModeModifiers
|
|
}
|
|
if lastMode == ModeNone {
|
|
lastMode = mode
|
|
} else if mode != lastMode {
|
|
break
|
|
}
|
|
pos++
|
|
}
|
|
|
|
textRange := core.NewTextRange(-1, -1)
|
|
if start == 0 {
|
|
textRange = core.NewTextRange(modifiers.Pos(), textRange.End())
|
|
}
|
|
if pos == len(modifiers.Nodes)-1 {
|
|
textRange = core.NewTextRange(textRange.Pos(), modifiers.End())
|
|
}
|
|
if allowDecorators || lastMode == ModeModifiers {
|
|
p.emitListItems(
|
|
(*Printer).emitModifierLike,
|
|
parentNode,
|
|
modifiers.Nodes[start:pos],
|
|
core.IfElse(lastMode == ModeModifiers, LFModifiers, LFDecorators),
|
|
false, /*hasTrailingComma*/
|
|
textRange,
|
|
)
|
|
}
|
|
start = pos
|
|
lastMode = mode
|
|
pos++
|
|
}
|
|
|
|
if p.OnAfterEmitNodeList != nil {
|
|
p.OnAfterEmitNodeList(&modifiers.NodeList)
|
|
}
|
|
}
|
|
|
|
return greatestEnd(parentNode.Pos(), modifiers, core.LastOrNil(modifiers.Nodes))
|
|
}
|
|
|
|
func (p *Printer) emitTypeParameter(node *ast.TypeParameterDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
if node.Constraint != nil {
|
|
p.writeSpace()
|
|
p.writeKeyword("extends")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.Constraint)
|
|
}
|
|
if node.DefaultType != nil {
|
|
p.writeSpace()
|
|
p.writeOperator("=")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.DefaultType)
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTypeParameterNode(node *ast.TypeParameterDeclarationNode) {
|
|
// NOTE: QuickInfo uses TypeFormatFlagsWriteTypeArgumentsOfSignature to instruct the NodeBuilder to store type arguments
|
|
// (i.e. type nodes) instead of type parameter declarations in the type parameter list.
|
|
if ast.IsTypeParameterDeclaration(node) {
|
|
p.emitTypeParameter(node.AsTypeParameter())
|
|
} else {
|
|
p.emitTypeArgument(node)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitParameterName(node *ast.BindingName) {
|
|
savedWriteKind := p.writeKind
|
|
p.writeKind = WriteKindParameter
|
|
p.emitBindingName(node)
|
|
p.writeKind = savedWriteKind
|
|
}
|
|
|
|
func (p *Printer) emitParameter(node *ast.ParameterDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
|
|
p.emitTokenNode(node.DotDotDotToken)
|
|
p.emitParameterName(node.Name())
|
|
p.emitTokenNode(node.QuestionToken)
|
|
|
|
p.emitTypeAnnotation(node.Type)
|
|
|
|
// The comment position has to fallback to any present node within the parameter declaration because as it turns
|
|
// out, the parser can make parameter declarations with _just_ an initializer.
|
|
p.emitInitializer(node.Initializer, greatestEnd(node.Pos(), node.Type, node.QuestionToken, node.Name(), node.Modifiers()), node.AsNode())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitParameterNode(node *ast.ParameterDeclarationNode) {
|
|
p.emitParameter(node.AsParameterDeclaration())
|
|
}
|
|
|
|
func (p *Printer) emitDecorator(node *ast.Decorator) {
|
|
p.writePunctuation("@")
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceMember)
|
|
}
|
|
|
|
func (p *Printer) emitModifierLike(node *ast.ModifierLike) {
|
|
switch {
|
|
case ast.IsDecorator(node):
|
|
p.emitDecorator(node.AsDecorator())
|
|
case ast.IsModifier(node):
|
|
p.emitKeywordNode(node)
|
|
default:
|
|
panic(fmt.Sprintf("unhandled ModifierLike: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitTypeParameters(parentNode *ast.Node, nodes *ast.TypeParameterList) {
|
|
if nodes == nil {
|
|
return
|
|
}
|
|
p.emitList((*Printer).emitTypeParameterNode, parentNode, nodes, LFTypeParameters|core.IfElse(ast.IsArrowFunction(parentNode) /*p.shouldAllowTrailingComma(parentNode, nodes)*/, LFAllowTrailingComma, LFNone)) // TODO: preserve trailing comma after Strada migration
|
|
}
|
|
|
|
func (p *Printer) emitTypeAnnotation(node *ast.TypeNode) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
p.writePunctuation(":")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node)
|
|
}
|
|
|
|
func (p *Printer) emitInitializer(node *ast.Expression, equalTokenPos int, contextNode *ast.Node) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindEqualsToken, equalTokenPos, WriteKindOperator, contextNode)
|
|
p.writeSpace()
|
|
p.emitExpression(node, ast.OperatorPrecedenceDisallowComma)
|
|
}
|
|
|
|
func (p *Printer) emitParameters(parentNode *ast.Node, parameters *ast.ParameterList) {
|
|
p.generateAllNames(parameters)
|
|
p.emitList((*Printer).emitParameterNode, parentNode, parameters, LFParameters /*|core.IfElse(p.shouldAllowTrailingComma(parentNode, parameters), LFAllowTrailingComma, LFNone)*/) // TODO: preserve trailing comma after Strada migration
|
|
}
|
|
|
|
func canEmitSimpleArrowHead(parentNode *ast.Node, parameters *ast.ParameterList) bool {
|
|
// only arrow functions with a single parameter may have simple arrow head
|
|
if !ast.IsArrowFunction(parentNode) || len(parameters.Nodes) != 1 {
|
|
return false
|
|
}
|
|
|
|
parent := parentNode.AsArrowFunction()
|
|
parameter := parameters.Nodes[0].AsParameterDeclaration()
|
|
|
|
return parameter.Pos() == greatestEnd(parent.Pos(), parent.Modifiers()) && // may not have parsed tokens between modifiers/start of parent and parameter
|
|
parent.TypeParameters == nil && // parent may not have type parameters
|
|
parent.Type == nil && // parent may not have return type annotation
|
|
!parameters.HasTrailingComma() && // parameters may not have a trailing comma
|
|
parameter.Modifiers() == nil && // parameter may not have decorators or modifiers
|
|
parameter.DotDotDotToken == nil && // parameter may not be rest
|
|
parameter.QuestionToken == nil && // parameter may not be optional
|
|
parameter.Type == nil && // parameter may not have a type annotation
|
|
parameter.Initializer == nil && // parameter may not have an initializer
|
|
ast.IsIdentifier(parameter.Name()) // parameter name must be identifier
|
|
}
|
|
|
|
func (p *Printer) emitParametersForArrow(parentNode *ast.Node /*FunctionTypeNode | ConstructorTypeNode | ArrowFunction*/, parameters *ast.ParameterList) {
|
|
if canEmitSimpleArrowHead(parentNode, parameters) {
|
|
p.generateAllNames(parameters)
|
|
p.emitList((*Printer).emitParameterNode, parentNode, parameters, LFSingleArrowParameter)
|
|
} else {
|
|
p.emitParameters(parentNode, parameters)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitParametersForIndexSignature(parentNode *ast.Node, parameters *ast.ParameterList) {
|
|
p.generateAllNames(parameters)
|
|
p.emitList((*Printer).emitParameterNode, parentNode, parameters, LFIndexSignatureParameters)
|
|
}
|
|
|
|
func (p *Printer) emitSignature(node *ast.Node) {
|
|
n := node.FunctionLikeData()
|
|
|
|
// !!! In old emitter, quickinfo used type arguments in place of type parameters on instantiated signatures
|
|
////if n.TypeArguments != nil {
|
|
//// p.emitTypeArguments(node, n.TypeArguments)
|
|
////} else {
|
|
p.emitTypeParameters(node, n.TypeParameters)
|
|
////}
|
|
|
|
p.emitParameters(node, n.Parameters)
|
|
p.emitTypeAnnotation(n.Type)
|
|
}
|
|
|
|
func (p *Printer) emitFunctionBody(body *ast.Block) {
|
|
state := p.enterNode(body.AsNode())
|
|
p.generateNames(body.AsNode())
|
|
|
|
// !!! Emit with comment after Strada migration
|
|
////p.emitTokenWithComment(ast.KindOpenBraceToken, body.Pos(), WriteKindPunctuation, body.AsNode())
|
|
p.writePunctuation("{")
|
|
|
|
p.increaseIndent()
|
|
detachedState := p.emitDetachedCommentsBeforeStatementList(body.AsNode(), body.Statements.Loc)
|
|
statementOffset := p.emitPrologueDirectives(body.Statements)
|
|
pos := p.writer.GetTextPos()
|
|
p.emitHelpers(body.AsNode())
|
|
|
|
if p.shouldEmitBlockFunctionBodyOnSingleLine(body) && statementOffset == 0 && pos == p.writer.GetTextPos() {
|
|
p.decreaseIndent()
|
|
p.emitList((*Printer).emitStatement, body.AsNode(), body.Statements, LFSingleLineFunctionBodyStatements)
|
|
p.increaseIndent()
|
|
} else {
|
|
p.emitList((*Printer).emitStatement, body.AsNode(), body.Statements, LFMultiLineFunctionBodyStatements)
|
|
}
|
|
|
|
p.emitDetachedCommentsAfterStatementList(body.AsNode(), body.Statements.Loc, detachedState)
|
|
p.decreaseIndent()
|
|
|
|
// !!! Emit comment after Strada migration
|
|
////p.emitTokenEx(ast.KindCloseBraceToken, body.Statements.End(), WriteKindPunctuation, body.AsNode(), tefNone)
|
|
p.emitTokenEx(ast.KindCloseBraceToken, body.Statements.End(), WriteKindPunctuation, body.AsNode(), tefNoComments)
|
|
|
|
p.exitNode(body.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitFunctionBodyNode(node *ast.BlockNode) {
|
|
if node == nil {
|
|
p.writeTrailingSemicolon()
|
|
return
|
|
}
|
|
|
|
p.writeSpace()
|
|
p.emitFunctionBody(node.AsBlock())
|
|
}
|
|
|
|
//
|
|
// Type Members
|
|
//
|
|
|
|
func (p *Printer) emitPropertySignature(node *ast.PropertySignatureDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.emitPropertyName(node.Name())
|
|
p.emitTokenNode(node.PostfixToken)
|
|
p.emitTypeAnnotation(node.Type)
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitPropertyDeclaration(node *ast.PropertyDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
|
|
p.emitPropertyName(node.Name())
|
|
p.emitTokenNode(node.PostfixToken)
|
|
p.emitTypeAnnotation(node.Type)
|
|
p.emitInitializer(node.Initializer, greatestEnd(node.Name().End(), node.Type, node.PostfixToken), node.AsNode())
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitMethodSignature(node *ast.MethodSignatureDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.emitPropertyName(node.Name())
|
|
p.emitTokenNode(node.PostfixToken)
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitSignature(node.AsNode())
|
|
p.writeTrailingSemicolon()
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitMethodDeclaration(node *ast.MethodDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
|
|
p.emitTokenNode(node.AsteriskToken)
|
|
p.emitPropertyName(node.Name())
|
|
p.emitTokenNode(node.PostfixToken)
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitSignature(node.AsNode())
|
|
p.emitFunctionBodyNode(node.Body)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitClassStaticBlockDeclaration(node *ast.ClassStaticBlockDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writeKeyword("static")
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitFunctionBodyNode(node.Body)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitConstructor(node *ast.ConstructorDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.writeKeyword("constructor")
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitSignature(node.AsNode())
|
|
p.emitFunctionBodyNode(node.Body)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitAccessorDeclaration(token ast.Kind, node *ast.AccessorDeclarationBase) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
|
|
p.emitToken(token, pos, WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitPropertyName(node.Name())
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitSignature(node.AsNode())
|
|
p.emitFunctionBodyNode(node.Body)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitGetAccessorDeclaration(node *ast.GetAccessorDeclaration) {
|
|
p.emitAccessorDeclaration(ast.KindGetKeyword, &node.AccessorDeclarationBase)
|
|
}
|
|
|
|
func (p *Printer) emitSetAccessorDeclaration(node *ast.SetAccessorDeclaration) {
|
|
p.emitAccessorDeclaration(ast.KindSetKeyword, &node.AccessorDeclarationBase)
|
|
}
|
|
|
|
func (p *Printer) emitCallSignature(node *ast.CallSignatureDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitSignature(node.AsNode())
|
|
p.writeTrailingSemicolon()
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitConstructSignature(node *ast.ConstructSignatureDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writeKeyword("new")
|
|
p.writeSpace()
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitSignature(node.AsNode())
|
|
p.writeTrailingSemicolon()
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitIndexSignature(node *ast.IndexSignatureDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitParametersForIndexSignature(node.AsNode(), node.Parameters)
|
|
p.emitTypeAnnotation(node.Type)
|
|
p.writeTrailingSemicolon()
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitClassElement(node *ast.ClassElement) {
|
|
switch node.Kind {
|
|
case ast.KindPropertyDeclaration:
|
|
p.emitPropertyDeclaration(node.AsPropertyDeclaration())
|
|
case ast.KindMethodDeclaration:
|
|
p.emitMethodDeclaration(node.AsMethodDeclaration())
|
|
case ast.KindClassStaticBlockDeclaration:
|
|
p.emitClassStaticBlockDeclaration(node.AsClassStaticBlockDeclaration())
|
|
case ast.KindConstructor:
|
|
p.emitConstructor(node.AsConstructorDeclaration())
|
|
case ast.KindGetAccessor:
|
|
p.emitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
|
|
case ast.KindSetAccessor:
|
|
p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
|
|
case ast.KindIndexSignature:
|
|
p.emitIndexSignature(node.AsIndexSignatureDeclaration())
|
|
case ast.KindSemicolonClassElement:
|
|
p.emitSemicolonClassElement(node.AsSemicolonClassElement())
|
|
case ast.KindNotEmittedStatement:
|
|
p.emitNotEmittedStatement(node.AsNotEmittedStatement())
|
|
case ast.KindJSTypeAliasDeclaration:
|
|
p.emitTypeAliasDeclaration(node.AsTypeAliasDeclaration())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected ClassElement: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitTypeElement(node *ast.TypeElement) {
|
|
switch node.Kind {
|
|
case ast.KindPropertySignature:
|
|
p.emitPropertySignature(node.AsPropertySignatureDeclaration())
|
|
case ast.KindMethodSignature:
|
|
p.emitMethodSignature(node.AsMethodSignatureDeclaration())
|
|
case ast.KindCallSignature:
|
|
p.emitCallSignature(node.AsCallSignatureDeclaration())
|
|
case ast.KindConstructSignature:
|
|
p.emitConstructSignature(node.AsConstructSignatureDeclaration())
|
|
case ast.KindGetAccessor:
|
|
p.emitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
|
|
case ast.KindSetAccessor:
|
|
p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
|
|
case ast.KindIndexSignature:
|
|
p.emitIndexSignature(node.AsIndexSignatureDeclaration())
|
|
case ast.KindNotEmittedTypeElement:
|
|
p.emitNotEmittedTypeElement(node.AsNotEmittedTypeElement())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected TypeElement: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitObjectLiteralElement(node *ast.ObjectLiteralElement) {
|
|
switch node.Kind {
|
|
case ast.KindPropertyAssignment:
|
|
p.emitPropertyAssignment(node.AsPropertyAssignment())
|
|
case ast.KindShorthandPropertyAssignment:
|
|
p.emitShorthandPropertyAssignment(node.AsShorthandPropertyAssignment())
|
|
case ast.KindSpreadAssignment:
|
|
p.emitSpreadAssignment(node.AsSpreadAssignment())
|
|
case ast.KindMethodDeclaration:
|
|
p.emitMethodDeclaration(node.AsMethodDeclaration())
|
|
case ast.KindGetAccessor:
|
|
p.emitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
|
|
case ast.KindSetAccessor:
|
|
p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled ObjectLiteralElement: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
//
|
|
// Types
|
|
//
|
|
|
|
func (p *Printer) emitKeywordTypeNode(node *ast.KeywordTypeNode) {
|
|
p.emitKeywordNode(node.AsNode())
|
|
}
|
|
|
|
func (p *Printer) emitTypePredicateParameterName(node *ast.TypePredicateParameterName) {
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierReference(node.AsIdentifier())
|
|
case ast.KindThisType:
|
|
p.emitThisType(node.AsThisTypeNode())
|
|
default:
|
|
panic(fmt.Sprintf("unexpected TypePredicateParameterName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitTypePredicate(node *ast.TypePredicateNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
if node.AssertsModifier != nil {
|
|
p.emitTokenNode(node.AssertsModifier)
|
|
p.writeSpace()
|
|
}
|
|
p.emitTypePredicateParameterName(node.ParameterName)
|
|
if node.Type != nil {
|
|
p.writeSpace()
|
|
p.writeKeyword("is")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTypeArgument(node *ast.TypeNode) {
|
|
p.emitTypeNodeOutsideExtends(node)
|
|
}
|
|
|
|
func (p *Printer) emitTypeArguments(parentNode *ast.Node, nodes *ast.TypeArgumentList) {
|
|
if nodes == nil {
|
|
return
|
|
}
|
|
p.emitList((*Printer).emitTypeArgument, parentNode, nodes, LFTypeArguments /*|core.IfElse(p.shouldAllowTrailingComma(parentNode, nodes), LFAllowTrailingComma, LFNone)*/) // TODO: preserve trailing comma after Strada migration
|
|
}
|
|
|
|
func (p *Printer) emitTypeReference(node *ast.TypeReferenceNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitEntityName(node.TypeName)
|
|
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
// Emits the return type of a FunctionTypeNode or ConstructorTypeNode, including the arrow (`=>`)
|
|
func (p *Printer) emitReturnType(node *ast.TypeNode) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
p.writePunctuation("=>")
|
|
p.writeSpace()
|
|
if p.inExtends && node.Kind == ast.KindInferType && node.AsInferTypeNode().TypeParameter.AsTypeParameter().Constraint != nil {
|
|
// if the parent FunctionTypeNode or ConstructorTypeNode is in the `extends` clause of a ConditionalTypeNode,
|
|
// we must parenthesize `infer ... extends ...` so as not to result in an ambiguous parse.
|
|
//
|
|
// `T extends () => infer U extends V ? W : X` would parse the `? W : X` as part of a ConditionalTypeNode in the
|
|
// return type of the FunctionTypeNode, thus we must emit as `T extends () => (infer U extends V) ? W : X`
|
|
p.emitTypeNodePreservingExtends(node, ast.TypePrecedenceHighest)
|
|
} else {
|
|
p.emitTypeNodePreservingExtends(node, ast.TypePrecedenceLowest)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitFunctionType(node *ast.FunctionTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
// !!! in the old emitter, quickinfo uses type arguments in place of type parameters for instantiated signatures
|
|
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
|
|
p.emitParameters(node.AsNode(), node.Parameters)
|
|
p.writeSpace()
|
|
p.emitReturnType(node.Type)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitConstructorType(node *ast.ConstructorTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.writeKeyword("new")
|
|
p.writeSpace()
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
// !!! in the old emitter, quickinfo uses type arguments in place of type parameters for instantiated signatures
|
|
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
|
|
p.emitParameters(node.AsNode(), node.Parameters)
|
|
p.writeSpace()
|
|
p.emitReturnType(node.Type)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTypeQuery(node *ast.TypeQueryNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writeKeyword("typeof")
|
|
p.writeSpace()
|
|
p.emitEntityName(node.ExprName)
|
|
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTypeLiteral(node *ast.TypeLiteralNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.generateAllMemberNames(node.Members)
|
|
p.writePunctuation("{")
|
|
flags := core.IfElse(p.shouldEmitOnSingleLine(node.AsNode()), LFSingleLineTypeLiteralMembers, LFMultiLineTypeLiteralMembers)
|
|
p.emitList((*Printer).emitTypeElement, node.AsNode(), node.Members, flags|LFNoSpaceIfEmpty)
|
|
p.writePunctuation("}")
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitArrayType(node *ast.ArrayTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitTypeNode(node.ElementType, ast.TypePrecedencePostfix)
|
|
p.writePunctuation("[")
|
|
p.writePunctuation("]")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTupleElementType(node *ast.Node) {
|
|
p.emitTypeNodeOutsideExtends(node)
|
|
}
|
|
|
|
func (p *Printer) emitTupleType(node *ast.TupleTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindOpenBracketToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
flags := core.IfElse(p.shouldEmitOnSingleLine(node.AsNode()), LFSingleLineTupleTypeElements, LFMultiLineTupleTypeElements)
|
|
p.emitList((*Printer).emitTupleElementType, node.AsNode(), node.Elements, flags|LFNoSpaceIfEmpty)
|
|
p.emitToken(ast.KindCloseBracketToken, node.Elements.End(), WriteKindPunctuation, node.AsNode())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitRestType(node *ast.RestTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("...")
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitOptionalType(node *ast.OptionalTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
// !!! May need extra parenthesization if we also have JSDocNullableType
|
|
p.emitTypeNode(node.Type, ast.TypePrecedencePostfix)
|
|
p.writePunctuation("?")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNamedTupleMember(node *ast.NamedTupleMember) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitPunctuationNode(node.DotDotDotToken)
|
|
p.emitIdentifierName(node.Name().AsIdentifier())
|
|
p.emitPunctuationNode(node.QuestionToken)
|
|
p.emitToken(ast.KindColonToken, greatestEnd(node.Name().End(), node.QuestionToken), WriteKindPunctuation, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitUnionTypeConstituent(node *ast.TypeNode) {
|
|
p.emitTypeNode(node, ast.TypePrecedenceTypeOperator)
|
|
}
|
|
|
|
func (p *Printer) emitUnionType(node *ast.UnionTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitList((*Printer).emitUnionTypeConstituent, node.AsNode(), node.Types, LFUnionTypeConstituents)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitIntersectionTypeConstituent(node *ast.TypeNode) {
|
|
p.emitTypeNode(node, ast.TypePrecedenceTypeOperator)
|
|
}
|
|
|
|
func (p *Printer) emitIntersectionType(node *ast.IntersectionTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitList((*Printer).emitIntersectionTypeConstituent, node.AsNode(), node.Types, LFIntersectionTypeConstituents /*, parenthesizer.parenthesizeConstituentTypeOfIntersectionType*/) // !!!
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitConditionalType(node *ast.ConditionalTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitTypeNode(node.CheckType, ast.TypePrecedenceUnion)
|
|
p.writeSpace()
|
|
p.writeKeyword("extends")
|
|
p.writeSpace()
|
|
p.emitTypeNodeInExtends(node.ExtendsType)
|
|
p.writeSpace()
|
|
p.writePunctuation("?")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.TrueType)
|
|
p.writeSpace()
|
|
p.writePunctuation(":")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.FalseType)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitInferTypeParameter(node *ast.TypeParameterDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
if node.Constraint != nil {
|
|
p.writeSpace()
|
|
p.writeKeyword("extends")
|
|
p.writeSpace()
|
|
p.emitTypeNodeInExtends(node.Constraint)
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitInferType(node *ast.InferTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writeKeyword("infer")
|
|
p.writeSpace()
|
|
p.emitInferTypeParameter(node.TypeParameter.AsTypeParameter())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitParenthesizedType(node *ast.ParenthesizedTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("(")
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
p.writePunctuation(")")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitThisType(node *ast.ThisTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writeKeyword("this")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTypeOperator(node *ast.TypeOperatorNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(node.Operator, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitTypeNode(node.Type, core.IfElse(node.Operator == ast.KindReadonlyKeyword, ast.TypePrecedencePostfix, ast.TypePrecedenceTypeOperator))
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitIndexedAccessType(node *ast.IndexedAccessTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitTypeNode(node.ObjectType, ast.TypePrecedencePostfix)
|
|
p.writePunctuation("[")
|
|
p.emitTypeNodeOutsideExtends(node.IndexType)
|
|
p.writePunctuation("]")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitMappedTypeParameter(node *ast.TypeParameterDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
p.writeSpace()
|
|
p.writeKeyword("in")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.Constraint)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitMappedType(node *ast.MappedTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
singleLine := p.shouldEmitOnSingleLine(node.AsNode())
|
|
p.writePunctuation("{")
|
|
if singleLine {
|
|
p.writeSpace()
|
|
} else {
|
|
p.writeLine()
|
|
p.increaseIndent()
|
|
}
|
|
if node.ReadonlyToken != nil {
|
|
p.emitTokenNode(node.ReadonlyToken)
|
|
if node.ReadonlyToken.Kind != ast.KindReadonlyKeyword {
|
|
p.writeKeyword("readonly")
|
|
}
|
|
p.writeSpace()
|
|
}
|
|
p.writePunctuation("[")
|
|
p.emitMappedTypeParameter(node.TypeParameter.AsTypeParameter())
|
|
if node.NameType != nil {
|
|
p.writeSpace()
|
|
p.writeKeyword("as")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.NameType)
|
|
}
|
|
p.writePunctuation("]")
|
|
if node.QuestionToken != nil {
|
|
p.emitPunctuationNode(node.QuestionToken)
|
|
if node.QuestionToken.Kind != ast.KindQuestionToken {
|
|
p.writePunctuation("?")
|
|
}
|
|
}
|
|
p.writePunctuation(":")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
p.writeTrailingSemicolon()
|
|
if node.Members != nil {
|
|
if singleLine {
|
|
p.writeSpace()
|
|
} else {
|
|
p.writeLine()
|
|
}
|
|
p.emitList((*Printer).emitTypeElement, node.AsNode(), node.Members, LFPreserveLines)
|
|
}
|
|
if singleLine {
|
|
p.writeSpace()
|
|
} else {
|
|
p.writeLine()
|
|
p.decreaseIndent()
|
|
}
|
|
p.writePunctuation("}")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitLiteralType(node *ast.LiteralTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Literal, ast.OperatorPrecedenceComma)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTemplateTypeSpan(node *ast.TemplateLiteralTypeSpan) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
p.emitTemplateMiddleTail(node.Literal)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTemplateTypeSpanNode(node *ast.TemplateLiteralTypeSpanNode) {
|
|
p.emitTemplateTypeSpan(node.AsTemplateLiteralTypeSpan())
|
|
}
|
|
|
|
func (p *Printer) emitTemplateType(node *ast.TemplateLiteralTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitTemplateHead(node.Head.AsTemplateHead())
|
|
p.emitList((*Printer).emitTemplateTypeSpanNode, node.AsNode(), node.TemplateSpans, LFTemplateExpressionSpans)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitImportTypeNodeAttributes(node *ast.ImportAttributes) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("{")
|
|
p.writeSpace()
|
|
p.writeKeyword(core.IfElse(node.Token == ast.KindAssertKeyword, "assert", "with"))
|
|
p.writePunctuation(":")
|
|
p.writeSpace()
|
|
p.emitList((*Printer).emitImportAttributeNode, node.AsNode(), node.Attributes, LFImportAttributes)
|
|
p.writeSpace()
|
|
p.writePunctuation("}")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitImportTypeNode(node *ast.ImportTypeNode) {
|
|
state := p.enterNode(node.AsNode())
|
|
if node.IsTypeOf {
|
|
p.writeKeyword("typeof")
|
|
p.writeSpace()
|
|
}
|
|
p.writeKeyword("import")
|
|
p.writePunctuation("(")
|
|
p.emitTypeNodeOutsideExtends(node.Argument)
|
|
if node.Attributes != nil {
|
|
p.writePunctuation(",")
|
|
p.writeSpace()
|
|
p.emitImportTypeNodeAttributes(node.Attributes.AsImportAttributes())
|
|
}
|
|
p.writePunctuation(")")
|
|
if node.Qualifier != nil {
|
|
p.writePunctuation(".")
|
|
p.emitEntityName(node.Qualifier)
|
|
}
|
|
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
// emits a Type node in the `extends` clause of a ConditionalType
|
|
func (p *Printer) emitTypeNodeInExtends(node *ast.TypeNode) {
|
|
savedInExtends := p.inExtends
|
|
p.inExtends = true
|
|
p.emitTypeNodePreservingExtends(node, ast.TypePrecedenceLowest)
|
|
p.inExtends = savedInExtends
|
|
}
|
|
|
|
// emits a Type node not in the `extends` clause of a ConditionalType or InferType
|
|
func (p *Printer) emitTypeNodeOutsideExtends(node *ast.TypeNode) {
|
|
savedInExtends := p.inExtends
|
|
p.inExtends = false
|
|
p.emitTypeNodePreservingExtends(node, ast.TypePrecedenceLowest)
|
|
p.inExtends = savedInExtends
|
|
}
|
|
|
|
// emits a Type node preserving whether or not we are currently in the `extends` clause of a ConditionalType or InferType
|
|
func (p *Printer) emitTypeNodePreservingExtends(node *ast.TypeNode, precedence ast.TypePrecedence) {
|
|
p.emitTypeNode(node, precedence)
|
|
}
|
|
|
|
func (p *Printer) emitTypeNode(node *ast.TypeNode, precedence ast.TypePrecedence) {
|
|
if p.inExtends && precedence <= ast.TypePrecedenceConditional {
|
|
// in the `extends` clause of a ConditionalType or InferType, a ConditionalType must be parenthesized
|
|
precedence = ast.TypePrecedenceFunction
|
|
}
|
|
|
|
savedInExtends := p.inExtends
|
|
parens := ast.GetTypeNodePrecedence(node) < precedence
|
|
if parens {
|
|
p.inExtends = false
|
|
p.writePunctuation("(")
|
|
}
|
|
|
|
switch node.Kind {
|
|
// Keyword Types
|
|
case ast.KindAnyKeyword,
|
|
ast.KindUnknownKeyword,
|
|
ast.KindNumberKeyword,
|
|
ast.KindBigIntKeyword,
|
|
ast.KindObjectKeyword,
|
|
ast.KindBooleanKeyword,
|
|
ast.KindStringKeyword,
|
|
ast.KindSymbolKeyword,
|
|
ast.KindVoidKeyword,
|
|
ast.KindUndefinedKeyword,
|
|
ast.KindNeverKeyword,
|
|
ast.KindIntrinsicKeyword:
|
|
p.emitKeywordTypeNode(node.AsKeywordTypeNode())
|
|
|
|
// Types
|
|
case ast.KindTypePredicate:
|
|
p.emitTypePredicate(node.AsTypePredicateNode())
|
|
case ast.KindTypeReference:
|
|
p.emitTypeReference(node.AsTypeReferenceNode())
|
|
case ast.KindFunctionType:
|
|
p.emitFunctionType(node.AsFunctionTypeNode())
|
|
case ast.KindConstructorType:
|
|
p.emitConstructorType(node.AsConstructorTypeNode())
|
|
case ast.KindTypeQuery:
|
|
p.emitTypeQuery(node.AsTypeQueryNode())
|
|
case ast.KindTypeLiteral:
|
|
p.emitTypeLiteral(node.AsTypeLiteralNode())
|
|
case ast.KindArrayType:
|
|
p.emitArrayType(node.AsArrayTypeNode())
|
|
case ast.KindTupleType:
|
|
p.emitTupleType(node.AsTupleTypeNode())
|
|
case ast.KindOptionalType:
|
|
p.emitOptionalType(node.AsOptionalTypeNode())
|
|
case ast.KindRestType:
|
|
p.emitRestType(node.AsRestTypeNode())
|
|
case ast.KindUnionType:
|
|
p.emitUnionType(node.AsUnionTypeNode())
|
|
case ast.KindIntersectionType:
|
|
p.emitIntersectionType(node.AsIntersectionTypeNode())
|
|
case ast.KindConditionalType:
|
|
p.emitConditionalType(node.AsConditionalTypeNode())
|
|
case ast.KindInferType:
|
|
p.emitInferType(node.AsInferTypeNode())
|
|
case ast.KindParenthesizedType:
|
|
p.emitParenthesizedType(node.AsParenthesizedTypeNode())
|
|
case ast.KindThisType:
|
|
p.emitThisType(node.AsThisTypeNode())
|
|
case ast.KindTypeOperator:
|
|
p.emitTypeOperator(node.AsTypeOperatorNode())
|
|
case ast.KindIndexedAccessType:
|
|
p.emitIndexedAccessType(node.AsIndexedAccessTypeNode())
|
|
case ast.KindMappedType:
|
|
p.emitMappedType(node.AsMappedTypeNode())
|
|
case ast.KindLiteralType:
|
|
p.emitLiteralType(node.AsLiteralTypeNode())
|
|
case ast.KindNamedTupleMember:
|
|
p.emitNamedTupleMember(node.AsNamedTupleMember())
|
|
case ast.KindTemplateLiteralType:
|
|
p.emitTemplateType(node.AsTemplateLiteralTypeNode())
|
|
case ast.KindTemplateLiteralTypeSpan:
|
|
p.emitTemplateTypeSpan(node.AsTemplateLiteralTypeSpan())
|
|
case ast.KindImportType:
|
|
p.emitImportTypeNode(node.AsImportTypeNode())
|
|
|
|
case ast.KindExpressionWithTypeArguments:
|
|
// !!! Should this actually be considered a type?
|
|
p.emitExpressionWithTypeArguments(node.AsExpressionWithTypeArguments())
|
|
|
|
case ast.KindJSDocAllType:
|
|
p.emitJSDocAllType(node)
|
|
case ast.KindJSDocNonNullableType:
|
|
p.emitJSDocNonNullableType(node.AsJSDocNonNullableType())
|
|
case ast.KindJSDocNullableType:
|
|
p.emitJSDocNullableType(node.AsJSDocNullableType())
|
|
case ast.KindJSDocOptionalType:
|
|
p.emitJSDocOptionalType(node.AsJSDocOptionalType())
|
|
case ast.KindJSDocVariadicType:
|
|
p.emitJSDocVariadicType(node.AsJSDocVariadicType())
|
|
|
|
default:
|
|
panic(fmt.Sprintf("unhandled TypeNode: %v", node.Kind))
|
|
}
|
|
|
|
if parens {
|
|
p.writePunctuation(")")
|
|
}
|
|
|
|
p.inExtends = savedInExtends
|
|
}
|
|
|
|
//
|
|
// Binding patterns
|
|
//
|
|
|
|
func (p *Printer) emitObjectBindingPattern(node *ast.BindingPattern) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("{")
|
|
p.emitList((*Printer).emitBindingElementNode, node.AsNode(), node.Elements, LFObjectBindingPatternElements)
|
|
p.writePunctuation("}")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitArrayBindingPattern(node *ast.BindingPattern) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("[")
|
|
p.emitList((*Printer).emitBindingElementNode, node.AsNode(), node.Elements, LFArrayBindingPatternElements)
|
|
p.writePunctuation("]")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitBindingElement(node *ast.BindingElement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitTokenNode(node.DotDotDotToken)
|
|
if node.PropertyName != nil {
|
|
p.emitPropertyName(node.PropertyName)
|
|
p.writePunctuation(":")
|
|
p.writeSpace()
|
|
}
|
|
// Old parser used `OmittedExpression` as a substitute for `Elision`. New parser uses a `BindingElement` with nil members
|
|
if name := node.Name(); name != nil {
|
|
p.emitBindingName(name)
|
|
p.emitInitializer(node.Initializer, node.Name().End(), node.AsNode())
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitBindingElementNode(node *ast.BindingElementNode) {
|
|
p.emitBindingElement(node.AsBindingElement())
|
|
}
|
|
|
|
func (p *Printer) emitJSDocAllType(node *ast.Node) {
|
|
p.emitKeywordNode(node)
|
|
}
|
|
|
|
func (p *Printer) emitJSDocNonNullableType(node *ast.JSDocNonNullableType) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("!")
|
|
p.emitTypeNode(node.Type, ast.TypePrecedenceNonArray)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJSDocNullableType(node *ast.JSDocNullableType) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("?")
|
|
p.emitTypeNode(node.Type, ast.TypePrecedenceNonArray)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJSDocOptionalType(node *ast.JSDocOptionalType) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitTypeNode(node.Type, ast.TypePrecedenceJSDoc)
|
|
p.writePunctuation("=")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJSDocVariadicType(node *ast.JSDocVariadicType) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("...")
|
|
p.emitTypeNode(node.Type, ast.TypePrecedenceJSDoc)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
//
|
|
// Expressions
|
|
//
|
|
|
|
func (p *Printer) emitKeywordExpression(node *ast.KeywordExpression) {
|
|
p.emitKeywordNode(node.AsNode())
|
|
}
|
|
|
|
func (p *Printer) emitArrayLiteralExpressionElement(node *ast.Expression) {
|
|
p.emitExpression(node, ast.OperatorPrecedenceSpread)
|
|
}
|
|
|
|
func (p *Printer) emitArrayLiteralExpression(node *ast.ArrayLiteralExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitList((*Printer).emitArrayLiteralExpressionElement, node.AsNode(), node.Elements, LFArrayLiteralExpressionElements|core.IfElse(node.MultiLine, LFPreferNewLine, LFNone))
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitObjectLiteralExpression(node *ast.ObjectLiteralExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.generateAllMemberNames(node.Properties)
|
|
p.emitList((*Printer).emitObjectLiteralElement, node.AsNode(), node.Properties, LFObjectLiteralExpressionProperties|
|
|
core.IfElse(node.MultiLine, LFPreferNewLine, LFNone)|
|
|
core.IfElse(p.shouldAllowTrailingComma(node.AsNode(), node.Properties), LFAllowTrailingComma, LFNone))
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
// 1..toString is a valid property access, emit a dot after the literal
|
|
// Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal
|
|
func (p *Printer) mayNeedDotDotForPropertyAccess(expression *ast.Expression) bool {
|
|
expression = ast.SkipPartiallyEmittedExpressions(expression)
|
|
if ast.IsNumericLiteral(expression) {
|
|
// check if numeric literal is a decimal literal that was originally written with a dot
|
|
text := p.getLiteralTextOfNode(expression /*sourceFile*/, nil, getLiteralTextFlagsNeverAsciiEscape)
|
|
// If the number will be printed verbatim and it doesn't already contain a dot or an exponent indicator, add one
|
|
// if the expression doesn't have any comments that will be emitted.
|
|
return expression.AsNumericLiteral().TokenFlags&ast.TokenFlagsWithSpecifier == 0 &&
|
|
!strings.Contains(text, scanner.TokenToString(ast.KindDotToken)) &&
|
|
!strings.Contains(text, "E") &&
|
|
!strings.Contains(text, "e")
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (p *Printer) emitPropertyAccessExpression(node *ast.PropertyAccessExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Expression, core.IfElse(ast.IsOptionalChain(node.AsNode()), ast.OperatorPrecedenceOptionalChain, ast.OperatorPrecedenceMember))
|
|
token := node.QuestionDotToken
|
|
if token == nil {
|
|
token = p.emitContext.Factory.NewToken(ast.KindDotToken)
|
|
token.Loc = core.NewTextRange(node.Expression.End(), node.Name().Pos())
|
|
p.emitContext.AddEmitFlags(token, EFNoSourceMap)
|
|
}
|
|
linesBeforeDot := p.getLinesBetweenNodes(node.AsNode(), node.Expression, token)
|
|
p.writeLineRepeat(linesBeforeDot)
|
|
p.increaseIndentIf(linesBeforeDot > 0)
|
|
shouldEmitDotDot := token.Kind != ast.KindQuestionDotToken &&
|
|
p.mayNeedDotDotForPropertyAccess(node.Expression) &&
|
|
!p.writer.HasTrailingComment() &&
|
|
!p.writer.HasTrailingWhitespace()
|
|
if shouldEmitDotDot {
|
|
p.writePunctuation(".")
|
|
}
|
|
p.emitTokenNode(token)
|
|
linesAfterDot := p.getLinesBetweenNodes(node.AsNode(), token, node.Name())
|
|
p.writeLineRepeat(linesAfterDot)
|
|
p.increaseIndentIf(linesAfterDot > 0)
|
|
p.emitMemberName(node.Name())
|
|
p.decreaseIndentIf(linesAfterDot > 0)
|
|
p.decreaseIndentIf(linesBeforeDot > 0)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitElementAccessExpression(node *ast.ElementAccessExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Expression, core.IfElse(ast.IsOptionalChain(node.AsNode()), ast.OperatorPrecedenceOptionalChain, ast.OperatorPrecedenceMember))
|
|
p.emitTokenNode(node.QuestionDotToken)
|
|
p.emitToken(ast.KindOpenBracketToken, greatestEnd(-1, node.Expression, node.QuestionDotToken), WriteKindPunctuation, node.AsNode())
|
|
p.emitExpression(node.ArgumentExpression, ast.OperatorPrecedenceComma)
|
|
p.emitToken(ast.KindCloseBracketToken, node.ArgumentExpression.End(), WriteKindPunctuation, node.AsNode())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitArgument(node *ast.Expression) {
|
|
p.emitExpression(node, ast.OperatorPrecedenceSpread)
|
|
}
|
|
|
|
func (p *Printer) emitCallee(callee *ast.Expression, parentNode *ast.Node) {
|
|
if p.shouldEmitIndirectCall(parentNode) {
|
|
p.writePunctuation("(")
|
|
p.writeLiteral("0")
|
|
p.writePunctuation(",")
|
|
p.writeSpace()
|
|
p.emitExpression(callee, ast.OperatorPrecedenceComma)
|
|
p.writePunctuation(")")
|
|
} else if parentNode.Kind == ast.KindCallExpression && isNewExpressionWithoutArguments(ast.SkipPartiallyEmittedExpressions(callee)) {
|
|
// Parenthesize `new C` inside of a CallExpression so it is treated as `(new C)()` and not `new C()`
|
|
p.emitExpression(callee, ast.OperatorPrecedenceParentheses)
|
|
} else {
|
|
p.emitExpression(callee, core.IfElse(ast.IsOptionalChain(parentNode), ast.OperatorPrecedenceOptionalChain, ast.OperatorPrecedenceMember))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitCallExpression(node *ast.CallExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitCallee(node.Expression, node.AsNode())
|
|
p.emitTokenNode(node.QuestionDotToken)
|
|
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
|
|
p.emitList((*Printer).emitArgument, node.AsNode(), node.Arguments, LFCallExpressionArguments)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNewExpression(node *ast.NewExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindNewKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
if ast.SkipPartiallyEmittedExpressions(node.Expression).Kind == ast.KindCallExpression {
|
|
// Parenthesize `C()` inside of a NewExpression so it is treated as `new (C())` and not `new C()`
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceParentheses)
|
|
} else {
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceMember)
|
|
}
|
|
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
|
|
p.emitList((*Printer).emitArgument, node.AsNode(), node.Arguments, LFNewExpressionArguments)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTemplateLiteral(node *ast.TemplateLiteral) {
|
|
switch node.Kind {
|
|
case ast.KindNoSubstitutionTemplateLiteral:
|
|
p.emitNoSubstitutionTemplateLiteral(node.AsNoSubstitutionTemplateLiteral())
|
|
case ast.KindTemplateExpression:
|
|
p.emitTemplateExpression(node.AsTemplateExpression())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled TemplateLiteral: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitTaggedTemplateExpression(node *ast.TaggedTemplateExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitCallee(node.Tag, node.AsNode())
|
|
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
|
|
p.writeSpace()
|
|
p.emitTemplateLiteral(node.Template)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTypeAssertionExpression(node *ast.TypeAssertion) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("<")
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
p.writePunctuation(">")
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceUpdate)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitParenthesizedExpression(node *ast.ParenthesizedExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
openParenPos := p.emitToken(ast.KindOpenParenToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
indented := p.writeLineSeparatorsAndIndentBefore(node.Expression, node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceComma)
|
|
p.writeLineSeparatorsAfter(node.Expression, node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.emitToken(ast.KindCloseParenToken, greatestEnd(openParenPos, node.Expression), WriteKindPunctuation, node.AsNode())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitFunctionExpression(node *ast.FunctionExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.generateNameIfNeeded(node.Name())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.writeKeyword("function")
|
|
p.emitTokenNode(node.AsteriskToken)
|
|
p.writeSpace()
|
|
p.emitIdentifierNameNode(node.Name())
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitSignature(node.AsNode())
|
|
p.emitFunctionBodyNode(node.Body)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitConciseBody(node *ast.BlockOrExpression) {
|
|
switch {
|
|
case ast.IsBlock(node):
|
|
p.emitFunctionBody(node.AsBlock())
|
|
case ast.IsObjectLiteralExpression(ast.GetLeftmostExpression(node, false /*stopAtCallExpressions*/)):
|
|
p.emitExpression(node, ast.OperatorPrecedenceParentheses)
|
|
case ast.IsExpression(node):
|
|
p.emitExpression(node, ast.OperatorPrecedenceYield)
|
|
default:
|
|
panic(fmt.Sprintf("unexpected ConciseBody: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitArrowFunction(node *ast.ArrowFunction) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
|
|
p.emitParametersForArrow(node.AsNode(), node.Parameters)
|
|
p.emitTypeAnnotation(node.Type)
|
|
p.writeSpace()
|
|
p.emitTokenNode(node.EqualsGreaterThanToken)
|
|
p.writeSpace()
|
|
p.emitConciseBody(node.Body)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitDeleteExpression(node *ast.DeleteExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindDeleteKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceUnary)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTypeOfExpression(node *ast.TypeOfExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindTypeOfKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceUnary)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitVoidExpression(node *ast.VoidExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindVoidKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceUnary)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitAwaitExpression(node *ast.AwaitExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindAwaitKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceUnary)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitPrefixUnaryExpression(node *ast.PrefixUnaryExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
operator := node.Operator
|
|
operand := node.Operand
|
|
p.emitToken(operator, node.Pos(), WriteKindOperator, node.AsNode())
|
|
|
|
// In some cases, we need to emit a space between the operator and the operand. One obvious case
|
|
// is when the operator is an identifier, like delete or typeof. We also need to do this for plus
|
|
// and minus expressions in certain cases. Specifically, consider the following two cases (parens
|
|
// are just for clarity of exposition, and not part of the source code):
|
|
//
|
|
// (+(+1))
|
|
// (+(++1))
|
|
//
|
|
// We need to emit a space in both cases. In the first case, the absence of a space will make
|
|
// the resulting expression a prefix increment operation. And in the second, it will make the resulting
|
|
// expression a prefix increment whose operand is a plus expression - (++(+x))
|
|
// The same is true of minus of course.
|
|
if operand.Kind == ast.KindPrefixUnaryExpression {
|
|
inner := operand.AsPrefixUnaryExpression().Operator
|
|
if (operator == ast.KindPlusToken && (inner == ast.KindPlusToken || inner == ast.KindPlusPlusToken)) ||
|
|
(operator == ast.KindMinusToken && (inner == ast.KindMinusToken || inner == ast.KindMinusMinusToken)) {
|
|
p.writeSpace()
|
|
}
|
|
}
|
|
|
|
p.emitExpression(node.Operand, ast.OperatorPrecedenceUnary)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitPostfixUnaryExpression(node *ast.PostfixUnaryExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Operand, ast.OperatorPrecedenceLeftHandSide)
|
|
p.emitToken(node.Operator, node.Operand.End(), WriteKindOperator, node.AsNode())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
// This function determines whether an expression consists of a homogeneous set of
|
|
// literal expressions or binary plus expressions that all share the same literal kind.
|
|
// It is used to determine whether the right-hand operand of a binary plus expression can be
|
|
// emitted without parentheses.
|
|
func (p *Printer) getLiteralKindOfBinaryPlusOperand(node *ast.Expression) ast.Kind {
|
|
node = ast.SkipPartiallyEmittedExpressions(node)
|
|
|
|
if ast.IsLiteralKind(node.Kind) {
|
|
return node.Kind
|
|
}
|
|
|
|
if node.Kind == ast.KindBinaryExpression {
|
|
if n := node.AsBinaryExpression(); n.OperatorToken.Kind == ast.KindPlusToken {
|
|
// !!! Determine if caching this is worthwhile over recomputing
|
|
////if n.cachedLiteralKind != KindUnknown {
|
|
//// return n.cachedLiteralKind;
|
|
////}
|
|
|
|
leftKind := p.getLiteralKindOfBinaryPlusOperand(n.Left)
|
|
literalKind := ast.KindUnknown
|
|
if ast.IsLiteralKind(leftKind) && leftKind == p.getLiteralKindOfBinaryPlusOperand(n.Right) {
|
|
literalKind = leftKind
|
|
}
|
|
|
|
////n.cachedLiteralKind = literalKind;
|
|
return literalKind
|
|
}
|
|
}
|
|
|
|
return ast.KindUnknown
|
|
}
|
|
|
|
func (p *Printer) getBinaryExpressionPrecedence(node *ast.BinaryExpression) (leftPrec ast.OperatorPrecedence, rightPrec ast.OperatorPrecedence) {
|
|
precedence := ast.GetExpressionPrecedence(node.AsNode())
|
|
leftPrec = precedence
|
|
rightPrec = precedence
|
|
switch precedence {
|
|
case ast.OperatorPrecedenceComma:
|
|
// No need to parenthesize the right operand when the binary operator and
|
|
// operand are both ,:
|
|
// x,(a,b) => x,a,b
|
|
break
|
|
case ast.OperatorPrecedenceAssignment:
|
|
// assignment is right-associative
|
|
leftPrec = ast.OperatorPrecedenceLeftHandSide
|
|
case ast.OperatorPrecedenceLogicalOR:
|
|
rightPrec = ast.OperatorPrecedenceLogicalAND
|
|
case ast.OperatorPrecedenceLogicalAND:
|
|
rightPrec = ast.OperatorPrecedenceBitwiseOR
|
|
case ast.OperatorPrecedenceBitwiseOR:
|
|
// No need to parenthesize the right operand when the binary operator and
|
|
// operand are both | due to the associative property of mathematics:
|
|
// x|(a|b) => x|a|b
|
|
break
|
|
case ast.OperatorPrecedenceBitwiseXOR:
|
|
// No need to parenthesize the right operand when the binary operator and
|
|
// operand are both ^ due to the associative property of mathematics:
|
|
// x^(a^b) => x^a^b
|
|
break
|
|
case ast.OperatorPrecedenceBitwiseAND:
|
|
// No need to parenthesize the right operand when the binary operator and
|
|
// operand are both & due to the associative property of mathematics:
|
|
// x&(a&b) => x&a&b
|
|
break
|
|
case ast.OperatorPrecedenceEquality:
|
|
rightPrec = ast.OperatorPrecedenceRelational
|
|
case ast.OperatorPrecedenceRelational:
|
|
rightPrec = ast.OperatorPrecedenceShift
|
|
case ast.OperatorPrecedenceShift:
|
|
rightPrec = ast.OperatorPrecedenceAdditive
|
|
case ast.OperatorPrecedenceAdditive:
|
|
if node.OperatorToken.Kind == ast.KindPlusToken && isBinaryOperation(node.Right, ast.KindPlusToken) {
|
|
leftKind := p.getLiteralKindOfBinaryPlusOperand(node.Left)
|
|
if ast.IsLiteralKind(leftKind) && leftKind == p.getLiteralKindOfBinaryPlusOperand(node.Right) {
|
|
// No need to parenthesize the right operand when the binary operator
|
|
// is plus (+) if both the left and right operands consist solely of either
|
|
// literals of the same kind or binary plus (+) expressions for literals of
|
|
// the same kind (recursively).
|
|
// "a"+(1+2) => "a"+(1+2)
|
|
// "a"+("b"+"c") => "a"+"b"+"c"
|
|
break
|
|
}
|
|
}
|
|
rightPrec = ast.OperatorPrecedenceMultiplicative
|
|
case ast.OperatorPrecedenceMultiplicative:
|
|
if node.OperatorToken.Kind == ast.KindAsteriskToken && isBinaryOperation(node.Right, ast.KindAsteriskToken) {
|
|
// No need to parenthesize the right operand when the binary operator and
|
|
// operand are both * due to the associative property of mathematics:
|
|
// x*(a*b) => x*a*b
|
|
break
|
|
}
|
|
rightPrec = ast.OperatorPrecedenceExponentiation
|
|
case ast.OperatorPrecedenceExponentiation:
|
|
// exponentiation is right-associative
|
|
leftPrec = ast.OperatorPrecedenceUpdate
|
|
default:
|
|
panic(fmt.Sprintf("unhandled precedence: %v", precedence))
|
|
}
|
|
return leftPrec, rightPrec
|
|
}
|
|
|
|
func (p *Printer) emitBinaryExpression(node *ast.BinaryExpression) {
|
|
leftPrec, rightPrec := p.getBinaryExpressionPrecedence(node)
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Left, leftPrec)
|
|
linesBeforeOperator := p.getLinesBetweenNodes(node.AsNode(), node.Left, node.OperatorToken)
|
|
linesAfterOperator := p.getLinesBetweenNodes(node.AsNode(), node.OperatorToken, node.Right)
|
|
p.writeLinesAndIndent(linesBeforeOperator, node.OperatorToken.Kind != ast.KindCommaToken /*writeSpaceIfNotIndenting*/)
|
|
p.emitTokenNodeEx(node.OperatorToken, tefNoSourceMaps)
|
|
p.writeLinesAndIndent(linesAfterOperator, true /*writeSpaceIfNotIndenting*/) // Binary operators should have a space before the comment starts
|
|
p.emitExpression(node.Right, rightPrec)
|
|
p.decreaseIndentIf(linesAfterOperator > 0)
|
|
p.decreaseIndentIf(linesBeforeOperator > 0)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitShortCircuitExpression(node *ast.Expression) {
|
|
if isBinaryOperation(ast.SkipPartiallyEmittedExpressions(node), ast.KindQuestionQuestionToken) {
|
|
p.emitExpression(node, ast.OperatorPrecedenceCoalesce)
|
|
} else {
|
|
p.emitExpression(node, ast.OperatorPrecedenceLogicalOR)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitConditionalExpression(node *ast.ConditionalExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
linesBeforeQuestion := p.getLinesBetweenNodes(node.AsNode(), node.Condition, node.QuestionToken)
|
|
linesAfterQuestion := p.getLinesBetweenNodes(node.AsNode(), node.QuestionToken, node.WhenTrue)
|
|
linesBeforeColon := p.getLinesBetweenNodes(node.AsNode(), node.WhenTrue, node.ColonToken)
|
|
linesAfterColon := p.getLinesBetweenNodes(node.AsNode(), node.ColonToken, node.WhenFalse)
|
|
p.emitShortCircuitExpression(node.Condition)
|
|
p.writeLinesAndIndent(linesBeforeQuestion /*writeSpaceIfNotIndenting*/, true)
|
|
p.emitPunctuationNode(node.QuestionToken)
|
|
p.writeLinesAndIndent(linesAfterQuestion /*writeSpaceIfNotIndenting*/, true)
|
|
p.emitExpression(node.WhenTrue, ast.OperatorPrecedenceYield)
|
|
p.decreaseIndentIf(linesAfterQuestion > 0)
|
|
p.decreaseIndentIf(linesBeforeQuestion > 0)
|
|
p.writeLinesAndIndent(linesBeforeColon /*writeSpaceIfNotIndenting*/, true)
|
|
p.emitPunctuationNode(node.ColonToken)
|
|
p.writeLinesAndIndent(linesAfterColon /*writeSpaceIfNotIndenting*/, true)
|
|
p.emitExpression(node.WhenFalse, ast.OperatorPrecedenceYield)
|
|
p.decreaseIndentIf(linesAfterColon > 0)
|
|
p.decreaseIndentIf(linesBeforeColon > 0)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTemplateExpression(node *ast.TemplateExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitTemplateHead(node.Head.AsTemplateHead())
|
|
p.emitList((*Printer).emitTemplateSpanNode, node.AsNode(), node.TemplateSpans, LFTemplateExpressionSpans)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitYieldExpression(node *ast.YieldExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindYieldKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.emitPunctuationNode(node.AsteriskToken)
|
|
if node.Expression != nil {
|
|
p.writeSpace()
|
|
p.emitExpressionNoASI(node.Expression, ast.OperatorPrecedenceDisallowComma)
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitSpreadElement(node *ast.SpreadElement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindDotDotDotToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitClassExpression(node *ast.ClassExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.generateNameIfNeeded(node.Name())
|
|
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
|
|
p.emitToken(ast.KindClassKeyword, greatestEnd(node.Pos(), node.Modifiers()), WriteKindKeyword, node.AsNode())
|
|
|
|
if node.Name() != nil {
|
|
p.writeSpace()
|
|
p.emitIdentifierName(node.Name().AsIdentifier())
|
|
}
|
|
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
|
|
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
|
|
p.emitList((*Printer).emitHeritageClauseNode, node.AsNode(), node.HeritageClauses, LFClassHeritageClauses)
|
|
p.writeSpace()
|
|
p.writePunctuation("{")
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.generateAllMemberNames(node.Members)
|
|
p.emitList((*Printer).emitClassElement, node.AsNode(), node.Members, LFClassMembers)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.writePunctuation("}")
|
|
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitOmittedExpression(node *ast.Node) {
|
|
p.exitNode(node, p.enterNode(node))
|
|
}
|
|
|
|
func (p *Printer) emitExpressionWithTypeArguments(node *ast.ExpressionWithTypeArguments) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceMember)
|
|
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitExpressionWithTypeArgumentsNode(node *ast.ExpressionWithTypeArgumentsNode) {
|
|
p.emitExpressionWithTypeArguments(node.AsExpressionWithTypeArguments())
|
|
}
|
|
|
|
func (p *Printer) emitAsExpression(node *ast.AsExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceRelational)
|
|
p.writeSpace()
|
|
p.writeKeyword("as")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitSatisfiesExpression(node *ast.SatisfiesExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceRelational)
|
|
p.writeSpace()
|
|
p.writeKeyword("satisfies")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNonNullExpression(node *ast.NonNullExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceMember)
|
|
p.writeOperator("!")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitMetaProperty(node *ast.MetaProperty) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(node.KeywordToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
p.writePunctuation(".")
|
|
p.emitIdentifierName(node.Name().AsIdentifier())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitPartiallyEmittedExpression(node *ast.PartiallyEmittedExpression, precedence ast.OperatorPrecedence) {
|
|
// avoid reprinting parens for nested partially emitted expressions
|
|
type entry struct {
|
|
node *ast.PartiallyEmittedExpression
|
|
state printerState
|
|
}
|
|
var stack core.Stack[entry]
|
|
for {
|
|
state := p.enterNode(node.AsNode())
|
|
stack.Push(entry{node, state})
|
|
if !ast.IsPartiallyEmittedExpression(node.Expression) {
|
|
break
|
|
}
|
|
node = node.Expression.AsPartiallyEmittedExpression()
|
|
}
|
|
|
|
p.emitExpression(node.Expression, precedence)
|
|
|
|
// unwind stack
|
|
for stack.Len() > 0 {
|
|
entry := stack.Pop()
|
|
p.exitNode(node.AsNode(), entry.state)
|
|
node = entry.node
|
|
}
|
|
}
|
|
|
|
func (p *Printer) willEmitLeadingNewLine(node *ast.Expression) bool {
|
|
return false // !!! check if node will emit a leading comment that contains a trailing newline
|
|
}
|
|
|
|
func (p *Printer) emitExpressionNoASI(node *ast.Expression, precedence ast.OperatorPrecedence) {
|
|
// !!! restore parens when necessary to ensure a leading single-line comment doesn't introduce ASI:
|
|
// function f() {
|
|
// return (// comment
|
|
// a as T
|
|
// )
|
|
// }
|
|
// If we do not restore the parens, we would produce the following incorrect output:
|
|
// function f() {
|
|
// return // comment
|
|
// a;
|
|
// }
|
|
// Due to ASI, this would result in a `return` with no value followed by an unreachable expression statement.
|
|
if !p.commentsDisabled && node.Kind == ast.KindPartiallyEmittedExpression && p.willEmitLeadingNewLine(node) {
|
|
// !!! if there is an original parse tree node, restore it with location to preserve comments and source maps.
|
|
p.emitExpression(node, ast.OperatorPrecedenceParentheses)
|
|
} else {
|
|
p.emitExpression(node, precedence)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitExpression(node *ast.Expression, precedence ast.OperatorPrecedence) {
|
|
parens := ast.GetExpressionPrecedence(ast.SkipPartiallyEmittedExpressions(node)) < precedence
|
|
if parens {
|
|
p.writePunctuation("(")
|
|
}
|
|
|
|
switch node.Kind {
|
|
// Keywords
|
|
case ast.KindTrueKeyword, ast.KindFalseKeyword, ast.KindNullKeyword:
|
|
p.emitTokenNode(node)
|
|
case ast.KindThisKeyword, ast.KindSuperKeyword, ast.KindImportKeyword:
|
|
p.emitKeywordExpression(node.AsKeywordExpression())
|
|
|
|
// Literals
|
|
case ast.KindNumericLiteral:
|
|
p.emitNumericLiteral(node.AsNumericLiteral())
|
|
case ast.KindBigIntLiteral:
|
|
p.emitBigIntLiteral(node.AsBigIntLiteral())
|
|
case ast.KindStringLiteral:
|
|
p.emitStringLiteral(node.AsStringLiteral())
|
|
case ast.KindRegularExpressionLiteral:
|
|
p.emitRegularExpressionLiteral(node.AsRegularExpressionLiteral())
|
|
case ast.KindNoSubstitutionTemplateLiteral:
|
|
p.emitNoSubstitutionTemplateLiteral(node.AsNoSubstitutionTemplateLiteral())
|
|
|
|
// Identifiers
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierReference(node.AsIdentifier())
|
|
case ast.KindPrivateIdentifier:
|
|
p.emitPrivateIdentifier(node.AsPrivateIdentifier())
|
|
|
|
// Expressions
|
|
case ast.KindArrayLiteralExpression:
|
|
p.emitArrayLiteralExpression(node.AsArrayLiteralExpression())
|
|
case ast.KindObjectLiteralExpression:
|
|
p.emitObjectLiteralExpression(node.AsObjectLiteralExpression())
|
|
case ast.KindPropertyAccessExpression:
|
|
p.emitPropertyAccessExpression(node.AsPropertyAccessExpression())
|
|
case ast.KindElementAccessExpression:
|
|
p.emitElementAccessExpression(node.AsElementAccessExpression())
|
|
case ast.KindCallExpression:
|
|
p.emitCallExpression(node.AsCallExpression())
|
|
case ast.KindNewExpression:
|
|
p.emitNewExpression(node.AsNewExpression())
|
|
case ast.KindTaggedTemplateExpression:
|
|
p.emitTaggedTemplateExpression(node.AsTaggedTemplateExpression())
|
|
case ast.KindTypeAssertionExpression:
|
|
p.emitTypeAssertionExpression(node.AsTypeAssertion())
|
|
case ast.KindParenthesizedExpression:
|
|
p.emitParenthesizedExpression(node.AsParenthesizedExpression())
|
|
case ast.KindFunctionExpression:
|
|
p.emitFunctionExpression(node.AsFunctionExpression())
|
|
case ast.KindArrowFunction:
|
|
p.emitArrowFunction(node.AsArrowFunction())
|
|
case ast.KindDeleteExpression:
|
|
p.emitDeleteExpression(node.AsDeleteExpression())
|
|
case ast.KindTypeOfExpression:
|
|
p.emitTypeOfExpression(node.AsTypeOfExpression())
|
|
case ast.KindVoidExpression:
|
|
p.emitVoidExpression(node.AsVoidExpression())
|
|
case ast.KindAwaitExpression:
|
|
p.emitAwaitExpression(node.AsAwaitExpression())
|
|
case ast.KindPrefixUnaryExpression:
|
|
p.emitPrefixUnaryExpression(node.AsPrefixUnaryExpression())
|
|
case ast.KindPostfixUnaryExpression:
|
|
p.emitPostfixUnaryExpression(node.AsPostfixUnaryExpression())
|
|
case ast.KindBinaryExpression:
|
|
p.emitBinaryExpression(node.AsBinaryExpression())
|
|
case ast.KindConditionalExpression:
|
|
p.emitConditionalExpression(node.AsConditionalExpression())
|
|
case ast.KindTemplateExpression:
|
|
p.emitTemplateExpression(node.AsTemplateExpression())
|
|
case ast.KindYieldExpression:
|
|
p.emitYieldExpression(node.AsYieldExpression())
|
|
case ast.KindSpreadElement:
|
|
p.emitSpreadElement(node.AsSpreadElement())
|
|
case ast.KindClassExpression:
|
|
p.emitClassExpression(node.AsClassExpression())
|
|
case ast.KindOmittedExpression:
|
|
p.emitOmittedExpression(node)
|
|
case ast.KindAsExpression:
|
|
p.emitAsExpression(node.AsAsExpression())
|
|
case ast.KindNonNullExpression:
|
|
p.emitNonNullExpression(node.AsNonNullExpression())
|
|
case ast.KindExpressionWithTypeArguments:
|
|
p.emitExpressionWithTypeArguments(node.AsExpressionWithTypeArguments())
|
|
case ast.KindSatisfiesExpression:
|
|
p.emitSatisfiesExpression(node.AsSatisfiesExpression())
|
|
case ast.KindMetaProperty:
|
|
p.emitMetaProperty(node.AsMetaProperty())
|
|
case ast.KindSyntheticExpression:
|
|
panic("SyntheticExpression should never be printed.")
|
|
case ast.KindMissingDeclaration:
|
|
break
|
|
|
|
// JSX
|
|
case ast.KindJsxElement:
|
|
p.emitJsxElement(node.AsJsxElement())
|
|
case ast.KindJsxSelfClosingElement:
|
|
p.emitJsxSelfClosingElement(node.AsJsxSelfClosingElement())
|
|
case ast.KindJsxFragment:
|
|
p.emitJsxFragment(node.AsJsxFragment())
|
|
|
|
// Synthesized list
|
|
case ast.KindSyntaxList:
|
|
panic("SyntaxList should not be printed")
|
|
|
|
// Transformation nodes
|
|
case ast.KindNotEmittedStatement:
|
|
return
|
|
case ast.KindPartiallyEmittedExpression:
|
|
p.emitPartiallyEmittedExpression(node.AsPartiallyEmittedExpression(), precedence)
|
|
case ast.KindSyntheticReferenceExpression:
|
|
panic("SyntheticReferenceExpression should not be printed")
|
|
|
|
// !!!
|
|
////case ast.KindCommaListExpression:
|
|
//// p.emitCommaList(node.AsCommaListExpression())
|
|
|
|
default:
|
|
panic(fmt.Sprintf("unexpected Expression: %v", node.Kind))
|
|
}
|
|
|
|
if parens {
|
|
p.writePunctuation(")")
|
|
}
|
|
}
|
|
|
|
//
|
|
// Misc
|
|
//
|
|
|
|
func (p *Printer) emitTemplateSpan(node *ast.TemplateSpan) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceComma)
|
|
p.emitTemplateMiddleTail(node.Literal)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTemplateSpanNode(node *ast.TemplateSpanNode) {
|
|
p.emitTemplateSpan(node.AsTemplateSpan())
|
|
}
|
|
|
|
func (p *Printer) emitSemicolonClassElement(node *ast.SemicolonClassElement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
//
|
|
// Statements
|
|
//
|
|
|
|
func (p *Printer) isEmptyBlock(block *ast.Node, statements *ast.StatementList) bool {
|
|
return len(statements.Nodes) == 0 &&
|
|
(p.currentSourceFile == nil || rangeEndIsOnSameLineAsRangeStart(block.Loc, block.Loc, p.currentSourceFile))
|
|
}
|
|
|
|
func (p *Printer) emitBlock(node *ast.Block) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.generateNames(node.AsNode())
|
|
p.emitToken(ast.KindOpenBraceToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
|
|
format := core.IfElse(!node.Multiline && p.isEmptyBlock(node.AsNode(), node.Statements) || p.shouldEmitOnSingleLine(node.AsNode()),
|
|
LFSingleLineBlockStatements,
|
|
LFMultiLineBlockStatements)
|
|
p.emitList((*Printer).emitStatement, node.AsNode(), node.Statements, format)
|
|
|
|
p.emitTokenEx(ast.KindCloseBraceToken, node.Statements.End(), WriteKindPunctuation, node.AsNode(), core.IfElse(format&LFMultiLine != 0, tefIndentLeadingComments, tefNone))
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitVariableStatement(node *ast.VariableStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.emitVariableDeclarationList(node.DeclarationList.AsVariableDeclarationList())
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitEmptyStatement(node *ast.EmptyStatement, isEmbeddedStatement bool) {
|
|
state := p.enterNode(node.AsNode())
|
|
|
|
// While most trailing semicolons are possibly insignificant, an embedded "empty"
|
|
// statement is significant and cannot be elided by a trailing-semicolon-omitting writer.
|
|
if isEmbeddedStatement {
|
|
p.writePunctuation(";")
|
|
} else {
|
|
p.writeTrailingSemicolon()
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitExpressionStatement(node *ast.ExpressionStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
|
|
if p.currentSourceFile != nil && p.currentSourceFile.ScriptKind == core.ScriptKindJSON {
|
|
// !!! In strada, this was handled by an undefined parenthesizerRule, so this is a hack.
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceComma)
|
|
} else if isImmediatelyInvokedFunctionExpressionOrArrowFunction(node.Expression) {
|
|
// !!! introduce parentheses around callee
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceParentheses)
|
|
} else {
|
|
switch ast.GetLeftmostExpression(node.Expression, false /*stopAtCallExpression*/).Kind {
|
|
case ast.KindFunctionExpression, ast.KindClassExpression, ast.KindObjectLiteralExpression:
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceParentheses)
|
|
default:
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceComma)
|
|
}
|
|
}
|
|
|
|
// Emit semicolon in non json files
|
|
// or if json file that created synthesized expression(eg.define expression statement when --out and amd code generation)
|
|
if p.currentSourceFile == nil ||
|
|
p.currentSourceFile.ScriptKind != core.ScriptKindJSON ||
|
|
ast.NodeIsSynthesized(node.Expression) {
|
|
p.writeTrailingSemicolon()
|
|
}
|
|
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitIfStatement(node *ast.IfStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitToken(ast.KindIfKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
|
|
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
|
|
p.emitEmbeddedStatement(node.AsNode(), node.ThenStatement)
|
|
if node.ElseStatement != nil {
|
|
p.writeLineOrSpace(node.AsNode(), node.ThenStatement, node.ElseStatement)
|
|
p.emitToken(ast.KindElseKeyword, node.ThenStatement.End(), WriteKindKeyword, node.AsNode())
|
|
if node.ElseStatement.Kind == ast.KindIfStatement {
|
|
p.writeSpace()
|
|
p.emitIfStatement(node.ElseStatement.AsIfStatement())
|
|
} else {
|
|
p.emitEmbeddedStatement(node.AsNode(), node.ElseStatement)
|
|
}
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitWhileClause(node *ast.Node, expression *ast.Expression, startPos int) {
|
|
pos := p.emitToken(ast.KindWhileKeyword, startPos, WriteKindKeyword, node)
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node)
|
|
p.emitExpression(expression, ast.OperatorPrecedenceLowest)
|
|
p.emitToken(ast.KindCloseParenToken, expression.End(), WriteKindPunctuation, node)
|
|
}
|
|
|
|
func (p *Printer) emitDoStatement(node *ast.DoStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindDoKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
|
|
if ast.IsBlock(node.Statement) && !p.Options.PreserveSourceNewlines {
|
|
p.writeSpace()
|
|
} else {
|
|
p.writeLineOrSpace(node.AsNode(), node.Statement, node.Expression)
|
|
}
|
|
|
|
p.emitWhileClause(node.AsNode(), node.Expression, node.Statement.End())
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitWhileStatement(node *ast.WhileStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitWhileClause(node.AsNode(), node.Expression, node.Pos())
|
|
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitForInitializer(node *ast.ForInitializer) {
|
|
if node.Kind == ast.KindVariableDeclarationList {
|
|
p.emitVariableDeclarationList(node.AsVariableDeclarationList())
|
|
} else {
|
|
p.emitExpression(node, ast.OperatorPrecedenceLowest)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitForStatement(node *ast.ForStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitToken(ast.KindForKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
pos = p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
|
|
if node.Initializer != nil {
|
|
p.emitForInitializer(node.Initializer)
|
|
pos = node.Initializer.End()
|
|
}
|
|
pos = p.emitToken(ast.KindSemicolonToken, pos, WriteKindPunctuation, node.AsNode())
|
|
if node.Condition != nil {
|
|
p.writeSpace()
|
|
p.emitExpression(node.Condition, ast.OperatorPrecedenceLowest)
|
|
pos = node.Condition.End()
|
|
}
|
|
pos = p.emitToken(ast.KindSemicolonToken, pos, WriteKindPunctuation, node.AsNode())
|
|
if node.Incrementor != nil {
|
|
p.writeSpace()
|
|
p.emitExpression(node.Incrementor, ast.OperatorPrecedenceLowest)
|
|
pos = node.Incrementor.End()
|
|
}
|
|
p.emitToken(ast.KindCloseParenToken, pos, WriteKindPunctuation, node.AsNode())
|
|
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitForInStatement(node *ast.ForInOrOfStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitToken(ast.KindForKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
|
|
p.emitForInitializer(node.Initializer)
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindInKeyword, node.Initializer.End(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
|
|
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
|
|
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitForOfStatement(node *ast.ForInOrOfStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
openParenPos := p.emitToken(ast.KindForKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
if node.AwaitModifier != nil {
|
|
p.emitKeywordNode(node.AwaitModifier)
|
|
p.writeSpace()
|
|
}
|
|
p.emitToken(ast.KindOpenParenToken, openParenPos, WriteKindPunctuation, node.AsNode())
|
|
p.emitForInitializer(node.Initializer)
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindOfKeyword, node.Initializer.End(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
|
|
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
|
|
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitContinueStatement(node *ast.ContinueStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindContinueKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
if node.Label != nil {
|
|
p.writeSpace()
|
|
p.emitLabelIdentifier(node.Label.AsIdentifier())
|
|
}
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitBreakStatement(node *ast.BreakStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindBreakKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
if node.Label != nil {
|
|
p.writeSpace()
|
|
p.emitLabelIdentifier(node.Label.AsIdentifier())
|
|
}
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitReturnStatement(node *ast.ReturnStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindReturnKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
if node.Expression != nil {
|
|
p.writeSpace()
|
|
p.emitExpressionNoASI(node.Expression, ast.OperatorPrecedenceLowest)
|
|
}
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitWithStatement(node *ast.WithStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitToken(ast.KindWithKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
|
|
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
|
|
p.emitEmbeddedStatement(node.AsNode(), node.Statement)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitSwitchStatement(node *ast.SwitchStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitToken(ast.KindSwitchKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindOpenParenToken, pos, WriteKindPunctuation, node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
|
|
p.emitToken(ast.KindCloseParenToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitCaseBlock(node.CaseBlock.AsCaseBlock())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitLabeledStatement(node *ast.LabeledStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitLabelIdentifier(node.Label.AsIdentifier())
|
|
p.emitToken(ast.KindColonToken, node.Label.End(), WriteKindPunctuation, node.AsNode())
|
|
|
|
// TODO: use emitEmbeddedStatement rather than writeSpace/emitStatement here after Strada migration as it is
|
|
// more consistent with similar emit elsewhere. writeSpace/emitStatement is used here to reduce spurious
|
|
// diffs when testing the Strada migration.
|
|
////p.emitEmbeddedStatement(node.AsNode(), node.Statement)
|
|
|
|
p.writeSpace()
|
|
p.emitStatement(node.Statement)
|
|
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitThrowStatement(node *ast.ThrowStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindThrowKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitExpressionNoASI(node.Expression, ast.OperatorPrecedenceLowest)
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTryStatement(node *ast.TryStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindTryKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitBlock(node.TryBlock.AsBlock())
|
|
if node.CatchClause != nil {
|
|
p.writeLineOrSpace(node.AsNode(), node.TryBlock, node.CatchClause)
|
|
p.emitCatchClause(node.CatchClause.AsCatchClause())
|
|
}
|
|
if node.FinallyBlock != nil {
|
|
p.writeLineOrSpace(node.AsNode(), core.Coalesce(node.CatchClause, node.TryBlock), node.FinallyBlock)
|
|
p.emitToken(ast.KindFinallyKeyword, core.Coalesce(node.CatchClause, node.TryBlock).End(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitBlock(node.FinallyBlock.AsBlock())
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitDebuggerStatement(node *ast.DebuggerStatement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindDebuggerKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNotEmittedStatement(node *ast.NotEmittedStatement) {
|
|
p.exitNode(node.AsNode(), p.enterNode(node.AsNode()))
|
|
}
|
|
|
|
func (p *Printer) emitNotEmittedTypeElement(node *ast.NotEmittedTypeElement) {
|
|
p.exitNode(node.AsNode(), p.enterNode(node.AsNode()))
|
|
}
|
|
|
|
//
|
|
// Declarations
|
|
//
|
|
|
|
func (p *Printer) emitVariableDeclaration(node *ast.VariableDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitBindingName(node.Name())
|
|
p.emitPunctuationNode(node.ExclamationToken)
|
|
p.emitTypeAnnotation(node.Type)
|
|
// !!! old compiler can set a type node purely for emit. Is this necessary?
|
|
p.emitInitializer(node.Initializer, greatestEnd(node.Name().End(), node.Type /*, node.Name().emitNode?.typeNode*/), node.AsNode())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitVariableDeclarationNode(node *ast.VariableDeclarationNode) {
|
|
p.emitVariableDeclaration(node.AsVariableDeclaration())
|
|
}
|
|
|
|
func (p *Printer) emitVariableDeclarationList(node *ast.VariableDeclarationList) {
|
|
state := p.enterNode(node.AsNode())
|
|
switch {
|
|
case ast.IsVarLet(node.AsNode()):
|
|
p.writeKeyword("let")
|
|
case ast.IsVarConst(node.AsNode()):
|
|
p.writeKeyword("const")
|
|
case ast.IsVarUsing(node.AsNode()):
|
|
p.writeKeyword("using")
|
|
case ast.IsVarAwaitUsing(node.AsNode()):
|
|
p.writeKeyword("await")
|
|
p.writeSpace()
|
|
p.writeKeyword("using")
|
|
default:
|
|
p.writeKeyword("var")
|
|
}
|
|
p.writeSpace()
|
|
p.emitList((*Printer).emitVariableDeclarationNode, node.AsNode(), node.Declarations, LFVariableDeclarationList)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitFunctionDeclaration(node *ast.FunctionDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.generateNameIfNeeded(node.Name())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.writeKeyword("function")
|
|
p.emitTokenNode(node.AsteriskToken)
|
|
p.writeSpace()
|
|
if name := node.Name(); name != nil {
|
|
p.emitIdentifierName(name.AsIdentifier())
|
|
}
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.emitSignature(node.AsNode())
|
|
p.emitFunctionBodyNode(node.Body)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitClassDeclaration(node *ast.ClassDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.generateNameIfNeeded(node.Name())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), true /*allowDecorators*/)
|
|
p.emitToken(ast.KindClassKeyword, greatestEnd(node.Pos(), node.Modifiers()), WriteKindKeyword, node.AsNode())
|
|
if node.Name() != nil {
|
|
p.writeSpace()
|
|
p.emitIdentifierName(node.Name().AsIdentifier())
|
|
}
|
|
indented := p.shouldEmitIndented(node.AsNode())
|
|
p.increaseIndentIf(indented)
|
|
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
|
|
p.emitList((*Printer).emitHeritageClauseNode, node.AsNode(), node.HeritageClauses, LFClassHeritageClauses)
|
|
p.writeSpace()
|
|
p.writePunctuation("{")
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.generateAllMemberNames(node.Members)
|
|
p.emitList((*Printer).emitClassElement, node.AsNode(), node.Members, LFClassMembers)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.writePunctuation("}")
|
|
p.decreaseIndentIf(indented)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitInterfaceDeclaration(node *ast.InterfaceDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.writeKeyword("interface")
|
|
p.writeSpace()
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
|
|
p.emitList((*Printer).emitHeritageClauseNode, node.AsNode(), node.HeritageClauses, LFHeritageClauses)
|
|
p.writeSpace()
|
|
p.writePunctuation("{")
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.generateAllMemberNames(node.Members)
|
|
p.emitList((*Printer).emitTypeElement, node.AsNode(), node.Members, LFInterfaceMembers)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.writePunctuation("}")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitTypeAliasDeclaration(node *ast.TypeAliasDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.writeKeyword("type")
|
|
p.writeSpace()
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
p.emitTypeParameters(node.AsNode(), node.TypeParameters)
|
|
p.writeSpace()
|
|
p.writePunctuation("=")
|
|
p.writeSpace()
|
|
p.emitTypeNodeOutsideExtends(node.Type)
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitEnumDeclaration(node *ast.EnumDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.writeKeyword("enum")
|
|
p.writeSpace()
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
p.writeSpace()
|
|
p.writePunctuation("{")
|
|
p.emitList((*Printer).emitEnumMemberNode, node.AsNode(), node.Members, LFEnumMembers)
|
|
p.writePunctuation("}")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitModuleDeclaration(node *ast.ModuleDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
if node.Keyword != ast.KindGlobalKeyword {
|
|
p.writeKeyword(core.IfElse(node.Keyword == ast.KindNamespaceKeyword, "namespace", "module"))
|
|
p.writeSpace()
|
|
}
|
|
p.emitModuleName(node.Name())
|
|
body := node.Body
|
|
for body != nil && ast.IsModuleDeclaration(body) {
|
|
module := body.AsModuleDeclaration()
|
|
p.writePunctuation(".")
|
|
p.emitNestedModuleName(module.Name())
|
|
body = module.Body
|
|
}
|
|
if body == nil {
|
|
p.writeTrailingSemicolon()
|
|
} else {
|
|
p.writeSpace()
|
|
p.emitModuleBlock(body.AsModuleBlock())
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitModuleBlock(node *ast.ModuleBlock) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.generateNames(node.AsNode())
|
|
p.emitToken(ast.KindOpenBraceToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
format := core.IfElse(p.isEmptyBlock(node.AsNode(), node.Statements) || p.shouldEmitOnSingleLine(node.AsNode()),
|
|
LFSingleLineBlockStatements,
|
|
LFMultiLineBlockStatements)
|
|
p.emitList((*Printer).emitStatement, node.AsNode(), node.Statements, format)
|
|
p.emitTokenEx(ast.KindCloseBraceToken, node.Statements.End(), WriteKindPunctuation, node.AsNode(), core.IfElse(format&LFMultiLine != 0, tefIndentLeadingComments, tefNone))
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitCaseBlock(node *ast.CaseBlock) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindOpenBraceToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
p.emitList((*Printer).emitCaseOrDefaultClauseNode, node.AsNode(), node.Clauses, LFCaseBlockClauses)
|
|
p.emitTokenEx(ast.KindCloseBraceToken, node.Clauses.End(), WriteKindPunctuation, node.AsNode(), tefIndentLeadingComments)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
pos := p.emitToken(ast.KindImportKeyword, greatestEnd(node.Pos(), node.Modifiers()), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
if node.IsTypeOnly {
|
|
p.emitToken(ast.KindTypeKeyword, pos, WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
}
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindEqualsToken, node.Name().End(), WriteKindPunctuation, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitModuleReference(node.ModuleReference)
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitModuleReference(node *ast.ModuleReference) {
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierReference(node.AsIdentifier())
|
|
case ast.KindQualifiedName:
|
|
p.emitQualifiedName(node.AsQualifiedName())
|
|
case ast.KindExternalModuleReference:
|
|
p.emitExternalModuleReference(node.AsExternalModuleReference())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled ModuleReference: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitImportDeclaration(node *ast.ImportDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
p.emitToken(ast.KindImportKeyword, greatestEnd(node.Pos(), node.Modifiers()), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
if node.ImportClause != nil {
|
|
p.emitImportClause(node.ImportClause.AsImportClause())
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindFromKeyword, node.ImportClause.End(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
}
|
|
p.emitExpression(node.ModuleSpecifier, ast.OperatorPrecedenceLowest)
|
|
if node.Attributes != nil {
|
|
p.writeSpace()
|
|
p.emitImportAttributes(node.Attributes.AsImportAttributes())
|
|
}
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitImportClause(node *ast.ImportClause) {
|
|
state := p.enterNode(node.AsNode())
|
|
if node.PhaseModifier != ast.KindUnknown {
|
|
p.emitToken(node.PhaseModifier, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
}
|
|
if name := node.Name(); name != nil {
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
if node.NamedBindings != nil {
|
|
p.emitToken(ast.KindCommaToken, name.End(), WriteKindPunctuation, node.AsNode())
|
|
p.writeSpace()
|
|
}
|
|
}
|
|
p.emitNamedImportBindings(node.NamedBindings)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNamespaceImport(node *ast.NamespaceImport) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitToken(ast.KindAsteriskToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindAsKeyword, pos, WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNamedImports(node *ast.NamedImports) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("{")
|
|
p.emitList((*Printer).emitImportSpecifierNode, node.AsNode(), node.Elements, LFNamedImportsOrExportsElements)
|
|
p.writePunctuation("}")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNamedImportBindings(node *ast.NamedImportBindings) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
switch node.Kind {
|
|
case ast.KindNamespaceImport:
|
|
p.emitNamespaceImport(node.AsNamespaceImport())
|
|
case ast.KindNamedImports:
|
|
p.emitNamedImports(node.AsNamedImports())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled NamedImportBindings: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitImportSpecifier(node *ast.ImportSpecifier) {
|
|
state := p.enterNode(node.AsNode())
|
|
if node.IsTypeOnly {
|
|
p.writeKeyword("type")
|
|
p.writeSpace()
|
|
}
|
|
if node.PropertyName != nil {
|
|
p.emitModuleExportName(node.PropertyName)
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindAsKeyword, node.PropertyName.End(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
}
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitImportSpecifierNode(node *ast.ImportSpecifierNode) {
|
|
p.emitImportSpecifier(node.AsImportSpecifier())
|
|
}
|
|
|
|
func (p *Printer) emitExportAssignment(node *ast.ExportAssignment) {
|
|
state := p.enterNode(node.AsNode())
|
|
nextPos := p.emitToken(ast.KindExportKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
if node.IsExportEquals {
|
|
p.emitToken(ast.KindEqualsToken, nextPos, WriteKindOperator, node.AsNode())
|
|
} else {
|
|
p.emitToken(ast.KindDefaultKeyword, nextPos, WriteKindKeyword, node.AsNode())
|
|
}
|
|
p.writeSpace()
|
|
if node.IsExportEquals {
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceAssignment)
|
|
} else {
|
|
// parenthesize `class` and `function` expressions so as not to conflict with exported `class` and `function` declarations
|
|
expr := ast.GetLeftmostExpression(node.Expression, false /*stopAtCallExpressions*/)
|
|
if ast.IsClassExpression(expr) || ast.IsFunctionExpression(expr) {
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceParentheses)
|
|
} else {
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceAssignment)
|
|
}
|
|
}
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
// export declare var <name> = <initialiser>;
|
|
func (p *Printer) emitCommonJSExport(node *ast.CommonJSExport) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindExportKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.writeKeyword("var")
|
|
p.writeSpace()
|
|
if node.Name().Kind == ast.KindStringLiteral {
|
|
// TODO: This doesn't work for illegal names.
|
|
p.write(node.Name().AsStringLiteral().Text)
|
|
} else {
|
|
p.emitBindingName(node.Name())
|
|
}
|
|
p.emitInitializer(node.Initializer, node.Name().End(), node.AsNode())
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitExportDeclaration(node *ast.ExportDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitModifierList(node.AsNode(), node.Modifiers(), false /*allowDecorators*/)
|
|
pos := p.emitToken(ast.KindExportKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
if node.IsTypeOnly {
|
|
pos = p.emitToken(ast.KindTypeKeyword, pos, WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
}
|
|
if node.ExportClause != nil {
|
|
p.emitNamedExportBindings(node.ExportClause)
|
|
} else {
|
|
pos = p.emitToken(ast.KindAsteriskToken, pos, WriteKindPunctuation, node.AsNode())
|
|
}
|
|
if node.ModuleSpecifier != nil {
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindFromKeyword, greatestEnd(pos, node.ExportClause), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitExpression(node.ModuleSpecifier, ast.OperatorPrecedenceLowest)
|
|
}
|
|
if node.Attributes != nil {
|
|
p.writeSpace()
|
|
p.emitImportAttributes(node.Attributes.AsImportAttributes())
|
|
}
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitImportAttributes(node *ast.ImportAttributes) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(node.Token, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitList((*Printer).emitImportAttributeNode, node.AsNode(), node.Attributes, LFImportAttributes)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitImportAttribute(node *ast.ImportAttribute) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitImportAttributeName(node.Name())
|
|
p.writePunctuation(":")
|
|
p.writeSpace()
|
|
/// !!! emit trailing comments of value
|
|
p.emitExpression(node.Value, ast.OperatorPrecedenceDisallowComma)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitImportAttributeNode(node *ast.ImportAttributeNode) {
|
|
p.emitImportAttribute(node.AsImportAttribute())
|
|
}
|
|
|
|
func (p *Printer) emitNamespaceExportDeclaration(node *ast.NamespaceExportDeclaration) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitToken(ast.KindExportKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
pos = p.emitToken(ast.KindAsKeyword, pos, WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindNamespaceKeyword, pos, WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitBindingIdentifier(node.Name().AsIdentifier())
|
|
p.writeTrailingSemicolon()
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNamespaceExport(node *ast.NamespaceExport) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitToken(ast.KindAsteriskToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindAsKeyword, pos, WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitModuleExportName(node.Name())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNamedExports(node *ast.NamedExports) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("{")
|
|
p.emitList((*Printer).emitExportSpecifierNode, node.AsNode(), node.Elements, LFNamedImportsOrExportsElements)
|
|
p.writePunctuation("}")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitNamedExportBindings(node *ast.NamedExportBindings) {
|
|
switch node.Kind {
|
|
case ast.KindNamespaceExport:
|
|
p.emitNamespaceExport(node.AsNamespaceExport())
|
|
case ast.KindNamedExports:
|
|
p.emitNamedExports(node.AsNamedExports())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled NamedExportBindings: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitExportSpecifier(node *ast.ExportSpecifier) {
|
|
state := p.enterNode(node.AsNode())
|
|
if node.IsTypeOnly {
|
|
p.writeKeyword("type")
|
|
p.writeSpace()
|
|
}
|
|
if node.PropertyName != nil {
|
|
p.emitModuleExportName(node.PropertyName)
|
|
p.writeSpace()
|
|
p.emitToken(ast.KindAsKeyword, node.PropertyName.End(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
}
|
|
p.emitModuleExportName(node.Name())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitExportSpecifierNode(node *ast.ExportSpecifierNode) {
|
|
p.emitExportSpecifier(node.AsExportSpecifier())
|
|
}
|
|
|
|
func (p *Printer) emitEmbeddedStatement(parentNode *ast.Node, node *ast.Statement) {
|
|
if ast.IsBlock(node) ||
|
|
p.shouldEmitOnSingleLine(parentNode) ||
|
|
p.Options.PreserveSourceNewlines && p.getLeadingLineTerminatorCount(parentNode, node, LFNone) == 0 {
|
|
p.writeSpace()
|
|
p.emitStatement(node)
|
|
} else {
|
|
p.writeLine()
|
|
p.increaseIndent()
|
|
if node.Kind == ast.KindEmptyStatement {
|
|
p.emitEmptyStatement(node.AsEmptyStatement(), true /*isEmbeddedStatement*/)
|
|
} else {
|
|
p.emitStatement(node)
|
|
}
|
|
p.decreaseIndent()
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitStatement(node *ast.Statement) {
|
|
switch node.Kind {
|
|
// Statements
|
|
case ast.KindBlock:
|
|
p.emitBlock(node.AsBlock())
|
|
case ast.KindEmptyStatement:
|
|
p.emitEmptyStatement(node.AsEmptyStatement(), false /*isEmbeddedStatement*/)
|
|
case ast.KindVariableStatement:
|
|
p.emitVariableStatement(node.AsVariableStatement())
|
|
case ast.KindExpressionStatement:
|
|
p.emitExpressionStatement(node.AsExpressionStatement())
|
|
case ast.KindIfStatement:
|
|
p.emitIfStatement(node.AsIfStatement())
|
|
case ast.KindDoStatement:
|
|
p.emitDoStatement(node.AsDoStatement())
|
|
case ast.KindWhileStatement:
|
|
p.emitWhileStatement(node.AsWhileStatement())
|
|
case ast.KindForStatement:
|
|
p.emitForStatement(node.AsForStatement())
|
|
case ast.KindForInStatement:
|
|
p.emitForInStatement(node.AsForInOrOfStatement())
|
|
case ast.KindForOfStatement:
|
|
p.emitForOfStatement(node.AsForInOrOfStatement())
|
|
case ast.KindContinueStatement:
|
|
p.emitContinueStatement(node.AsContinueStatement())
|
|
case ast.KindBreakStatement:
|
|
p.emitBreakStatement(node.AsBreakStatement())
|
|
case ast.KindReturnStatement:
|
|
p.emitReturnStatement(node.AsReturnStatement())
|
|
case ast.KindWithStatement:
|
|
p.emitWithStatement(node.AsWithStatement())
|
|
case ast.KindSwitchStatement:
|
|
p.emitSwitchStatement(node.AsSwitchStatement())
|
|
case ast.KindLabeledStatement:
|
|
p.emitLabeledStatement(node.AsLabeledStatement())
|
|
case ast.KindThrowStatement:
|
|
p.emitThrowStatement(node.AsThrowStatement())
|
|
case ast.KindTryStatement:
|
|
p.emitTryStatement(node.AsTryStatement())
|
|
case ast.KindDebuggerStatement:
|
|
p.emitDebuggerStatement(node.AsDebuggerStatement())
|
|
case ast.KindNotEmittedStatement:
|
|
p.emitNotEmittedStatement(node.AsNotEmittedStatement())
|
|
|
|
// Declaration Statements
|
|
case ast.KindFunctionDeclaration:
|
|
p.emitFunctionDeclaration(node.AsFunctionDeclaration())
|
|
case ast.KindClassDeclaration:
|
|
p.emitClassDeclaration(node.AsClassDeclaration())
|
|
case ast.KindInterfaceDeclaration:
|
|
p.emitInterfaceDeclaration(node.AsInterfaceDeclaration())
|
|
case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
|
|
p.emitTypeAliasDeclaration(node.AsTypeAliasDeclaration())
|
|
case ast.KindEnumDeclaration:
|
|
p.emitEnumDeclaration(node.AsEnumDeclaration())
|
|
case ast.KindModuleDeclaration:
|
|
p.emitModuleDeclaration(node.AsModuleDeclaration())
|
|
case ast.KindMissingDeclaration:
|
|
break
|
|
|
|
// Import/Export Statements
|
|
case ast.KindNamespaceExportDeclaration:
|
|
p.emitNamespaceExportDeclaration(node.AsNamespaceExportDeclaration())
|
|
case ast.KindImportEqualsDeclaration:
|
|
p.emitImportEqualsDeclaration(node.AsImportEqualsDeclaration())
|
|
case ast.KindImportDeclaration:
|
|
p.emitImportDeclaration(node.AsImportDeclaration())
|
|
case ast.KindExportAssignment, ast.KindJSExportAssignment:
|
|
p.emitExportAssignment(node.AsExportAssignment())
|
|
case ast.KindExportDeclaration:
|
|
p.emitExportDeclaration(node.AsExportDeclaration())
|
|
case ast.KindCommonJSExport:
|
|
p.emitCommonJSExport(node.AsCommonJSExport())
|
|
|
|
default:
|
|
panic(fmt.Sprintf("unhandled statement: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
//
|
|
// Module references
|
|
//
|
|
|
|
func (p *Printer) emitExternalModuleReference(node *ast.ExternalModuleReference) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writeKeyword("require")
|
|
p.writePunctuation("(")
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
|
|
p.writePunctuation(")")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
//
|
|
// JSX
|
|
//
|
|
|
|
func (p *Printer) emitJsxElement(node *ast.JsxElement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitJsxOpeningElement(node.OpeningElement.AsJsxOpeningElement())
|
|
p.emitList((*Printer).emitJsxChild, node.AsNode(), node.Children, LFJsxElementOrFragmentChildren)
|
|
p.emitJsxClosingElement(node.ClosingElement.AsJsxClosingElement())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxSelfClosingElement(node *ast.JsxSelfClosingElement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("<")
|
|
p.emitJsxTagName(node.TagName)
|
|
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
|
|
p.writeSpace()
|
|
p.emitJsxAttributes(node.Attributes.AsJsxAttributes())
|
|
p.writePunctuation("/>")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxFragment(node *ast.JsxFragment) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitJsxOpeningFragment(node.OpeningFragment.AsJsxOpeningFragment())
|
|
p.emitList((*Printer).emitJsxChild, node.AsNode(), node.Children, LFJsxElementOrFragmentChildren)
|
|
p.emitJsxClosingFragment(node.ClosingFragment.AsJsxClosingFragment())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxOpeningElement(node *ast.JsxOpeningElement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("<")
|
|
indented := p.writeLineSeparatorsAndIndentBefore(node.TagName, node.AsNode())
|
|
p.emitJsxTagName(node.TagName)
|
|
p.emitTypeArguments(node.AsNode(), node.TypeArguments)
|
|
if attributes := node.Attributes.AsJsxAttributes(); len(attributes.Properties.Nodes) > 0 {
|
|
p.writeSpace()
|
|
}
|
|
p.emitJsxAttributes(node.Attributes.AsJsxAttributes())
|
|
p.writeLineSeparatorsAfter(node.Attributes, node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
p.writePunctuation(">")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxClosingElement(node *ast.JsxClosingElement) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("</")
|
|
p.emitJsxTagName(node.TagName)
|
|
p.writePunctuation(">")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxOpeningFragment(node *ast.JsxOpeningFragment) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("<")
|
|
p.writePunctuation(">")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxClosingFragment(node *ast.JsxClosingFragment) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("</")
|
|
p.writePunctuation(">")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxText(node *ast.JsxText) {
|
|
state := p.enterNode(node.AsNode())
|
|
// TODO(rbuckton): Should this be using `getLiteralTextOfNode` instead?
|
|
p.writeLiteral(node.Text)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxAttributes(node *ast.JsxAttributes) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitList((*Printer).emitJsxAttributeLike, node.AsNode(), node.Properties, LFJsxElementAttributes)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxAttribute(node *ast.JsxAttribute) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitJsxAttributeName(node.Name())
|
|
if node.Initializer != nil {
|
|
p.writePunctuation("=")
|
|
p.emitJsxAttributeValue(node.Initializer)
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxSpreadAttribute(node *ast.JsxSpreadAttribute) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writePunctuation("{...")
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
|
|
p.writePunctuation("}")
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxAttributeLike(node *ast.JsxAttributeLike) {
|
|
switch node.Kind {
|
|
case ast.KindJsxAttribute:
|
|
p.emitJsxAttribute(node.AsJsxAttribute())
|
|
case ast.KindJsxSpreadAttribute:
|
|
p.emitJsxSpreadAttribute(node.AsJsxSpreadAttribute())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled JsxAttributeLike: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitJsxExpression(node *ast.JsxExpression) {
|
|
state := p.enterNode(node.AsNode())
|
|
if node.Expression != nil || !p.commentsDisabled && !ast.NodeIsSynthesized(node.AsNode()) && p.hasCommentsAtPosition(node.Pos()) { // preserve empty expressions if they contain comments!
|
|
indented := p.currentSourceFile != nil && !ast.NodeIsSynthesized(node.AsNode()) && GetLinesBetweenPositions(p.currentSourceFile, node.Pos(), node.End()) != 0
|
|
p.increaseIndentIf(indented)
|
|
end := p.emitToken(ast.KindOpenBraceToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
p.emitTokenNode(node.DotDotDotToken)
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
|
|
p.emitToken(ast.KindCloseBraceToken, greatestEnd(end, node.Expression, node.DotDotDotToken), WriteKindPunctuation, node.AsNode())
|
|
p.decreaseIndentIf(indented)
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxNamespacedName(node *ast.JsxNamespacedName) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitIdentifierName(node.Namespace.AsIdentifier())
|
|
p.writePunctuation(":")
|
|
p.emitIdentifierName(node.Name().AsIdentifier())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitJsxChild(node *ast.JsxChild) {
|
|
switch node.Kind {
|
|
case ast.KindJsxText:
|
|
p.emitJsxText(node.AsJsxText())
|
|
case ast.KindJsxExpression:
|
|
p.emitJsxExpression(node.AsJsxExpression())
|
|
case ast.KindJsxElement:
|
|
p.emitJsxElement(node.AsJsxElement())
|
|
case ast.KindJsxSelfClosingElement:
|
|
p.emitJsxSelfClosingElement(node.AsJsxSelfClosingElement())
|
|
case ast.KindJsxFragment:
|
|
p.emitJsxFragment(node.AsJsxFragment())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled JsxChild: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitJsxTagName(node *ast.JsxTagNameExpression) {
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierReference(node.AsIdentifier())
|
|
case ast.KindThisKeyword:
|
|
p.emitKeywordExpression(node.AsKeywordExpression())
|
|
case ast.KindJsxNamespacedName:
|
|
p.emitJsxNamespacedName(node.AsJsxNamespacedName())
|
|
case ast.KindPropertyAccessExpression:
|
|
p.emitPropertyAccessExpression(node.AsPropertyAccessExpression())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled JsxTagName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitJsxAttributeName(node *ast.JsxAttributeName) {
|
|
switch node.Kind {
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierName(node.AsIdentifier())
|
|
case ast.KindJsxNamespacedName:
|
|
p.emitJsxNamespacedName(node.AsJsxNamespacedName())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled JsxAttributeName: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitJsxAttributeValue(node *ast.JsxAttributeValue) {
|
|
switch node.Kind {
|
|
case ast.KindStringLiteral:
|
|
p.emitStringLiteral(node.AsStringLiteral())
|
|
case ast.KindJsxExpression:
|
|
p.emitJsxExpression(node.AsJsxExpression())
|
|
case ast.KindJsxElement:
|
|
p.emitJsxElement(node.AsJsxElement())
|
|
case ast.KindJsxSelfClosingElement:
|
|
p.emitJsxSelfClosingElement(node.AsJsxSelfClosingElement())
|
|
case ast.KindJsxFragment:
|
|
p.emitJsxFragment(node.AsJsxFragment())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled JsxAttributeValue: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clauses
|
|
//
|
|
|
|
func (p *Printer) emitCaseOrDefaultClauseStatements(node *ast.CaseOrDefaultClause) {
|
|
emitAsSingleStatement := len(node.Statements.Nodes) == 1 &&
|
|
// treat synthesized nodes as located on the same line for emit purposes
|
|
(p.currentSourceFile == nil ||
|
|
ast.NodeIsSynthesized(node.AsNode()) ||
|
|
ast.NodeIsSynthesized(node.Statements.Nodes[0]) ||
|
|
rangeStartPositionsAreOnSameLine(node.Loc, node.Statements.Nodes[0].Loc, p.currentSourceFile))
|
|
|
|
format := LFCaseOrDefaultClauseStatements
|
|
if emitAsSingleStatement {
|
|
p.writeSpace()
|
|
format &= ^(LFMultiLine | LFIndented)
|
|
}
|
|
|
|
p.emitList((*Printer).emitStatement, node.AsNode(), node.Statements, format)
|
|
}
|
|
|
|
func (p *Printer) emitCaseClause(node *ast.CaseOrDefaultClause) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitToken(ast.KindCaseKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceLowest)
|
|
p.emitToken(ast.KindColonToken, node.Expression.End(), WriteKindPunctuation, node.AsNode())
|
|
p.emitCaseOrDefaultClauseStatements(node)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitDefaultClause(node *ast.CaseOrDefaultClause) {
|
|
state := p.enterNode(node.AsNode())
|
|
pos := p.emitToken(ast.KindDefaultKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.emitToken(ast.KindColonToken, pos, WriteKindPunctuation, node.AsNode())
|
|
p.emitCaseOrDefaultClauseStatements(node)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitCaseOrDefaultClauseNode(node *ast.CaseOrDefaultClauseNode) {
|
|
switch node.Kind {
|
|
case ast.KindCaseClause:
|
|
p.emitCaseClause(node.AsCaseOrDefaultClause())
|
|
case ast.KindDefaultClause:
|
|
p.emitDefaultClause(node.AsCaseOrDefaultClause())
|
|
default:
|
|
panic(fmt.Sprintf("unhandled CaseOrDefaultClause: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitHeritageClause(node *ast.HeritageClause) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.writeSpace()
|
|
p.emitToken(node.Token, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
p.emitList((*Printer).emitExpressionWithTypeArgumentsNode, node.AsNode(), node.Types, LFHeritageClauseTypes)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitHeritageClauseNode(node *ast.HeritageClauseNode) {
|
|
p.emitHeritageClause(node.AsHeritageClause())
|
|
}
|
|
|
|
func (p *Printer) emitCatchClause(node *ast.CatchClause) {
|
|
state := p.enterNode(node.AsNode())
|
|
openParenPos := p.emitToken(ast.KindCatchKeyword, node.Pos(), WriteKindKeyword, node.AsNode())
|
|
p.writeSpace()
|
|
|
|
if node.VariableDeclaration != nil {
|
|
p.emitToken(ast.KindOpenParenToken, openParenPos, WriteKindPunctuation, node.AsNode())
|
|
p.emitVariableDeclaration(node.VariableDeclaration.AsVariableDeclaration())
|
|
p.emitToken(ast.KindCloseParenToken, node.VariableDeclaration.End(), WriteKindPunctuation, node.AsNode())
|
|
p.writeSpace()
|
|
}
|
|
|
|
p.emitBlock(node.Block.AsBlock())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
//
|
|
// Property assignments
|
|
//
|
|
|
|
func (p *Printer) emitPropertyAssignment(node *ast.PropertyAssignment) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitPropertyName(node.Name())
|
|
p.writePunctuation(":")
|
|
p.writeSpace()
|
|
// This is to ensure that we emit comment in the following case:
|
|
// For example:
|
|
// obj = {
|
|
// id: /*comment1*/ ()=>void
|
|
// }
|
|
// "comment1" is not considered to be leading comment for node.initializer
|
|
// but rather a trailing comment on the previous node.
|
|
initializer := node.Initializer
|
|
|
|
// !!! emit trailing comments of initializer
|
|
|
|
p.emitExpression(initializer, ast.OperatorPrecedenceDisallowComma)
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitShorthandPropertyAssignment(node *ast.ShorthandPropertyAssignment) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitPropertyName(node.Name())
|
|
if node.ObjectAssignmentInitializer != nil {
|
|
p.writeSpace()
|
|
p.writePunctuation("=")
|
|
p.writeSpace()
|
|
p.emitExpression(node.ObjectAssignmentInitializer, ast.OperatorPrecedenceDisallowComma)
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitSpreadAssignment(node *ast.SpreadAssignment) {
|
|
state := p.enterNode(node.AsNode())
|
|
if node.Expression != nil {
|
|
p.emitToken(ast.KindDotDotDotToken, node.Pos(), WriteKindPunctuation, node.AsNode())
|
|
p.emitExpression(node.Expression, ast.OperatorPrecedenceDisallowComma)
|
|
}
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
//
|
|
// Enum
|
|
//
|
|
|
|
func (p *Printer) emitEnumMember(node *ast.EnumMember) {
|
|
state := p.enterNode(node.AsNode())
|
|
p.emitPropertyName(node.Name())
|
|
p.emitInitializer(node.Initializer, node.Name().End(), node.AsNode())
|
|
p.exitNode(node.AsNode(), state)
|
|
}
|
|
|
|
func (p *Printer) emitEnumMemberNode(node *ast.EnumMemberNode) {
|
|
p.emitEnumMember(node.AsEnumMember())
|
|
}
|
|
|
|
//
|
|
// JSDoc
|
|
//
|
|
|
|
func (p *Printer) emitJSDocNode(node *ast.Node) {
|
|
// !!!
|
|
panic("not implemented")
|
|
}
|
|
|
|
//
|
|
// Top-level nodes
|
|
//
|
|
|
|
func (p *Printer) emitShebangIfNeeded(node *ast.SourceFile) {
|
|
shebang := scanner.GetShebang(node.Text())
|
|
if shebang != "" {
|
|
p.writeComment(shebang)
|
|
p.writeLine()
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitPrologueDirectives(statements *ast.StatementList) int {
|
|
for i, statement := range statements.Nodes {
|
|
if ast.IsPrologueDirective(statement) {
|
|
p.writeLine()
|
|
p.emitStatement(statement)
|
|
} else {
|
|
return i
|
|
}
|
|
}
|
|
return len(statements.Nodes)
|
|
}
|
|
|
|
func (p *Printer) emitHelpers(node *ast.Node) bool {
|
|
helpersEmitted := false
|
|
sourceFile := p.currentSourceFile
|
|
shouldSkip := p.Options.NoEmitHelpers || (sourceFile != nil && p.emitContext.HasRecordedExternalHelpers(sourceFile))
|
|
helpers := slices.Clone(p.emitContext.GetEmitHelpers(node))
|
|
if len(helpers) > 0 {
|
|
slices.SortStableFunc(helpers, compareEmitHelpers)
|
|
for _, helper := range helpers {
|
|
if !helper.Scoped {
|
|
// Skip the helper if it can be skipped and the noEmitHelpers compiler
|
|
// option is set, or if it can be imported and the importHelpers compiler
|
|
// option is set.
|
|
if shouldSkip {
|
|
continue
|
|
}
|
|
}
|
|
if helper.TextCallback != nil {
|
|
p.writeLines(helper.TextCallback(p.makeFileLevelOptimisticUniqueName))
|
|
} else {
|
|
p.writeLines(helper.Text)
|
|
}
|
|
helpersEmitted = true
|
|
}
|
|
}
|
|
|
|
return helpersEmitted
|
|
}
|
|
|
|
func (p *Printer) emitSourceFile(node *ast.SourceFile) {
|
|
savedCurrentSourceFile := p.currentSourceFile
|
|
savedCommentsDisabled := p.commentsDisabled
|
|
p.currentSourceFile = node
|
|
|
|
p.writeLine()
|
|
|
|
state := p.emitDetachedCommentsBeforeStatementList(node.AsNode(), node.Statements.Loc)
|
|
p.pushNameGenerationScope(node.AsNode())
|
|
p.generateAllNames(node.Statements)
|
|
|
|
index := 0
|
|
if node.ScriptKind != core.ScriptKindJSON {
|
|
p.emitShebangIfNeeded(node)
|
|
index = p.emitPrologueDirectives(node.Statements)
|
|
p.emitHelpers(node.AsNode())
|
|
if node.IsDeclarationFile {
|
|
p.emitTripleSlashDirectives(node)
|
|
}
|
|
}
|
|
|
|
// !!! Emit triple-slash directives
|
|
p.emitListRange(
|
|
(*Printer).emitStatement,
|
|
node.AsNode(),
|
|
node.Statements,
|
|
LFMultiLine,
|
|
index,
|
|
-1, /*count*/
|
|
)
|
|
p.popNameGenerationScope(node.AsNode())
|
|
p.emitDetachedCommentsAfterStatementList(node.AsNode(), node.Statements.Loc, state)
|
|
p.currentSourceFile = savedCurrentSourceFile
|
|
p.commentsDisabled = savedCommentsDisabled
|
|
}
|
|
|
|
func (p *Printer) emitTripleSlashDirectives(node *ast.SourceFile) {
|
|
p.emitDirective("path", node.ReferencedFiles)
|
|
p.emitDirective("types", node.TypeReferenceDirectives)
|
|
p.emitDirective("lib", node.LibReferenceDirectives)
|
|
}
|
|
|
|
func (p *Printer) emitDirective(kind string, refs []*ast.FileReference) {
|
|
for _, ref := range refs {
|
|
var resolutionMode string
|
|
if ref.ResolutionMode != core.ResolutionModeNone {
|
|
resolutionMode = fmt.Sprintf(`resolution-mode="%s" `, core.IfElse(ref.ResolutionMode == core.ResolutionModeESM, "import", "require"))
|
|
}
|
|
p.writeComment(fmt.Sprintf("/// <reference %s=\"%s\" %s%s/>", kind, ref.FileName, resolutionMode, core.IfElse(ref.Preserve, `preserve="true" `, "")))
|
|
p.writeLine()
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lists
|
|
//
|
|
|
|
func (p *Printer) emitList(emit func(p *Printer, node *ast.Node), parentNode *ast.Node, children *ast.NodeList, format ListFormat) {
|
|
if p.shouldEmitOnMultipleLines(parentNode) {
|
|
format |= LFPreferNewLine
|
|
}
|
|
|
|
p.emitListRange(emit, parentNode, children, format, -1 /*start*/, -1 /*count*/)
|
|
}
|
|
|
|
func (p *Printer) emitListRange(emit func(p *Printer, node *ast.Node), parentNode *ast.Node, children *ast.NodeList, format ListFormat, start int, count int) {
|
|
isNil := children == nil
|
|
|
|
length := 0
|
|
if !isNil {
|
|
length = len(children.Nodes)
|
|
}
|
|
|
|
if start < 0 {
|
|
start = 0
|
|
}
|
|
|
|
if count < 0 {
|
|
count = length - start
|
|
}
|
|
|
|
if isNil && format&LFOptionalIfNil != 0 {
|
|
return
|
|
}
|
|
|
|
isEmpty := isNil || start >= length || count <= 0
|
|
if isEmpty && format&LFOptionalIfEmpty != 0 {
|
|
if p.OnBeforeEmitNodeList != nil {
|
|
p.OnBeforeEmitNodeList(children)
|
|
}
|
|
if p.OnAfterEmitNodeList != nil {
|
|
p.OnAfterEmitNodeList(children)
|
|
}
|
|
return
|
|
}
|
|
|
|
if format&LFBracketsMask != 0 {
|
|
p.writePunctuation(getOpeningBracket(format))
|
|
if isEmpty && !isNil {
|
|
p.emitTrailingComments(children.Pos(), commentSeparatorBefore) // Emit comments within empty lists
|
|
}
|
|
}
|
|
|
|
if p.OnBeforeEmitNodeList != nil {
|
|
p.OnBeforeEmitNodeList(children)
|
|
}
|
|
|
|
if isEmpty {
|
|
// Write a line terminator if the parent node was multi-line
|
|
if format&LFMultiLine != 0 && !(p.Options.PreserveSourceNewlines && (parentNode == nil || p.currentSourceFile != nil && rangeIsOnSingleLine(parentNode.Loc, p.currentSourceFile))) {
|
|
p.writeLine()
|
|
} else if format&LFSpaceBetweenBraces != 0 && format&LFNoSpaceIfEmpty == 0 {
|
|
p.writeSpace()
|
|
}
|
|
} else {
|
|
end := min(start+count, length)
|
|
|
|
p.emitListItems(emit, parentNode, children.Nodes[start:end], format, p.hasTrailingComma(parentNode, children), children.Loc)
|
|
}
|
|
|
|
if p.OnAfterEmitNodeList != nil {
|
|
p.OnAfterEmitNodeList(children)
|
|
}
|
|
|
|
if format&LFBracketsMask != 0 {
|
|
if isEmpty && !isNil {
|
|
p.emitTrailingComments(children.Pos(), commentSeparatorBefore) // Emit comments within empty lists
|
|
}
|
|
p.writePunctuation(getClosingBracket(format))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) hasTrailingComma(parentNode *ast.Node, children *ast.NodeList) bool {
|
|
// NodeList.HasTrailingComma() is unreliable on transformed nodes as some nodes may have been removed. In the event
|
|
// we believe we may need to emit a trailing comma, we must first look to the respective node list on the original
|
|
// node first.
|
|
if !children.HasTrailingComma() {
|
|
return false
|
|
}
|
|
|
|
originalParent := p.emitContext.MostOriginal(parentNode)
|
|
if originalParent == parentNode {
|
|
// if this node is the original node, we can trust the result
|
|
return true
|
|
}
|
|
|
|
if originalParent.Kind != parentNode.Kind {
|
|
// if the original node is some other kind of node, we cannot correlate the list
|
|
return false
|
|
}
|
|
|
|
// find the respective node list on the original parent
|
|
originalList := children
|
|
switch originalParent.Kind {
|
|
case ast.KindObjectLiteralExpression:
|
|
originalList = originalParent.AsObjectLiteralExpression().Properties
|
|
case ast.KindArrayLiteralExpression:
|
|
originalList = originalParent.AsArrayLiteralExpression().Elements
|
|
case ast.KindCallExpression, ast.KindNewExpression:
|
|
switch children {
|
|
case parentNode.TypeArgumentList():
|
|
originalList = originalParent.TypeArgumentList()
|
|
case parentNode.ArgumentList():
|
|
originalList = originalParent.ArgumentList()
|
|
}
|
|
case ast.KindConstructor,
|
|
ast.KindMethodDeclaration,
|
|
ast.KindGetAccessor,
|
|
ast.KindSetAccessor,
|
|
ast.KindFunctionDeclaration,
|
|
ast.KindFunctionExpression,
|
|
ast.KindArrowFunction,
|
|
ast.KindFunctionType,
|
|
ast.KindConstructorType,
|
|
ast.KindCallSignature,
|
|
ast.KindConstructSignature:
|
|
switch children {
|
|
case parentNode.TypeParameterList():
|
|
originalList = originalParent.TypeParameterList()
|
|
case parentNode.ParameterList():
|
|
originalList = originalParent.ParameterList()
|
|
}
|
|
case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
|
|
switch children {
|
|
case parentNode.TypeParameterList():
|
|
originalList = originalParent.TypeParameterList()
|
|
}
|
|
case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern:
|
|
switch children {
|
|
case parentNode.AsBindingPattern().Elements:
|
|
originalList = originalParent.AsBindingPattern().Elements
|
|
}
|
|
case ast.KindNamedImports:
|
|
originalList = originalParent.AsNamedImports().Elements
|
|
case ast.KindNamedExports:
|
|
originalList = originalParent.AsNamedExports().Elements
|
|
case ast.KindImportAttributes:
|
|
originalList = originalParent.AsImportAttributes().Attributes
|
|
}
|
|
|
|
// if we have the original list, we can use it's result.
|
|
if originalList != nil {
|
|
return originalList.HasTrailingComma()
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (p *Printer) writeDelimiter(format ListFormat) {
|
|
switch format & LFDelimitersMask {
|
|
case LFNone:
|
|
break
|
|
case LFCommaDelimited:
|
|
p.writePunctuation(",")
|
|
case LFBarDelimited:
|
|
p.writeSpace()
|
|
p.writePunctuation("|")
|
|
case LFAsteriskDelimited:
|
|
p.writeSpace()
|
|
p.writePunctuation("*")
|
|
p.writeSpace()
|
|
case LFAmpersandDelimited:
|
|
p.writeSpace()
|
|
p.writePunctuation("&")
|
|
}
|
|
}
|
|
|
|
// Emits a list without brackets or raising events.
|
|
//
|
|
// NOTE: You probably don't want to call this directly and should be using `emitList` instead.
|
|
func (p *Printer) emitListItems(
|
|
emit func(p *Printer, node *ast.Node),
|
|
parentNode *ast.Node,
|
|
children []*ast.Node,
|
|
format ListFormat,
|
|
hasTrailingComma bool,
|
|
childrenTextRange core.TextRange,
|
|
) {
|
|
// Write the opening line terminator or leading whitespace.
|
|
mayEmitInterveningComments := format&LFNoInterveningComments == 0
|
|
shouldEmitInterveningComments := mayEmitInterveningComments
|
|
|
|
leadingLineTerminatorCount := 0
|
|
if len(children) > 0 {
|
|
leadingLineTerminatorCount = p.getLeadingLineTerminatorCount(parentNode, children[0], format)
|
|
}
|
|
if leadingLineTerminatorCount > 0 {
|
|
for range leadingLineTerminatorCount {
|
|
p.writeLine()
|
|
}
|
|
shouldEmitInterveningComments = false
|
|
} else if format&LFSpaceBetweenBraces != 0 {
|
|
p.writeSpace()
|
|
}
|
|
|
|
// Increase the indent, if requested.
|
|
if format&LFIndented != 0 {
|
|
p.increaseIndent()
|
|
}
|
|
|
|
parentEnd := greatestEnd(-1, parentNode)
|
|
|
|
// Emit each child.
|
|
var previousSibling *ast.Node
|
|
shouldDecreaseIndentAfterEmit := false
|
|
for _, child := range children {
|
|
// Write the delimiter if this is not the first node.
|
|
if format&LFAsteriskDelimited != 0 {
|
|
// always write JSDoc in the format "\n *"
|
|
p.writeLine()
|
|
p.writeDelimiter(format)
|
|
} else if previousSibling != nil {
|
|
// i.e
|
|
// function commentedParameters(
|
|
// /* Parameter a */
|
|
// a
|
|
// /* End of parameter a */ -> this comment isn't considered to be trailing comment of parameter "a" due to newline
|
|
// ,
|
|
if format&LFDelimitersMask != 0 && previousSibling.End() != parentEnd {
|
|
if !p.commentsDisabled && p.shouldEmitTrailingComments(previousSibling) {
|
|
p.emitLeadingComments(previousSibling.End(), false /*elided*/)
|
|
}
|
|
}
|
|
|
|
p.writeDelimiter(format)
|
|
|
|
// Write either a line terminator or whitespace to separate the elements.
|
|
separatingLineTerminatorCount := p.getSeparatingLineTerminatorCount(previousSibling, child, format)
|
|
if separatingLineTerminatorCount > 0 {
|
|
// If a synthesized node in a single-line list starts on a new
|
|
// line, we should increase the indent.
|
|
if format&(LFLinesMask|LFIndented) == LFSingleLine {
|
|
p.increaseIndent()
|
|
shouldDecreaseIndentAfterEmit = true
|
|
}
|
|
|
|
if shouldEmitInterveningComments && format&LFDelimitersMask != 0 && !ast.PositionIsSynthesized(child.Pos()) {
|
|
commentRange := getCommentRange(child)
|
|
p.emitTrailingComments(commentRange.Pos(), core.IfElse(format&LFSpaceBetweenSiblings != 0, commentSeparatorBefore, commentSeparatorNone))
|
|
}
|
|
|
|
for range separatingLineTerminatorCount {
|
|
p.writeLine()
|
|
}
|
|
|
|
shouldEmitInterveningComments = false
|
|
} else if format&LFSpaceBetweenSiblings != 0 {
|
|
p.writeSpace()
|
|
}
|
|
}
|
|
|
|
// Emit this child.
|
|
if shouldEmitInterveningComments {
|
|
commentRange := getCommentRange(child)
|
|
p.emitTrailingComments(commentRange.Pos(), commentSeparatorAfter)
|
|
} else {
|
|
shouldEmitInterveningComments = mayEmitInterveningComments
|
|
}
|
|
|
|
p.nextListElementPos = child.Pos()
|
|
emit(p, child)
|
|
|
|
if shouldDecreaseIndentAfterEmit {
|
|
p.decreaseIndent()
|
|
shouldDecreaseIndentAfterEmit = false
|
|
}
|
|
|
|
previousSibling = child
|
|
}
|
|
|
|
// Write a trailing comma, if requested.
|
|
skipTrailingComments := p.commentsDisabled || !p.shouldEmitTrailingComments(previousSibling)
|
|
emitTrailingComma := hasTrailingComma && format&LFAllowTrailingComma != 0 && format&LFCommaDelimited != 0
|
|
if emitTrailingComma {
|
|
if previousSibling != nil && !skipTrailingComments {
|
|
p.emitToken(ast.KindCommaToken, previousSibling.End(), WriteKindPunctuation, previousSibling)
|
|
} else {
|
|
p.writePunctuation(",")
|
|
}
|
|
}
|
|
|
|
// Emit any trailing comment of the last element in the list
|
|
// i.e
|
|
// var array = [...
|
|
// 2
|
|
// /* end of element 2 */
|
|
// ];
|
|
if previousSibling != nil && parentEnd != previousSibling.End() && format&LFDelimitersMask != 0 && !skipTrailingComments {
|
|
p.emitLeadingComments(greatestEnd(previousSibling.End(), childrenTextRange), false /*elided*/)
|
|
}
|
|
|
|
// Decrease the indent, if requested.
|
|
if format&LFIndented != 0 {
|
|
p.decreaseIndent()
|
|
}
|
|
|
|
// Write the closing line terminator or closing whitespace.
|
|
closingLineTerminatorCount := p.getClosingLineTerminatorCount(parentNode, core.LastOrNil(children), format, childrenTextRange)
|
|
if closingLineTerminatorCount > 0 {
|
|
for range closingLineTerminatorCount {
|
|
p.writeLine()
|
|
}
|
|
} else if format&(LFSpaceAfterList|LFSpaceBetweenBraces) != 0 {
|
|
p.writeSpace()
|
|
}
|
|
}
|
|
|
|
//
|
|
// General
|
|
//
|
|
|
|
func (p *Printer) Emit(node *ast.Node, sourceFile *ast.SourceFile) string {
|
|
// ensure a reusable writer
|
|
if p.ownWriter == nil {
|
|
p.ownWriter = NewTextWriter(p.Options.NewLine.GetNewLineCharacter())
|
|
}
|
|
|
|
p.Write(node, sourceFile, p.ownWriter, nil /*sourceMapGenerator*/)
|
|
text := p.ownWriter.String()
|
|
|
|
p.ownWriter.Clear()
|
|
return text
|
|
}
|
|
|
|
func (p *Printer) EmitSourceFile(sourceFile *ast.SourceFile) string {
|
|
return p.Emit(sourceFile.AsNode(), sourceFile)
|
|
}
|
|
|
|
func (p *Printer) setSourceFile(sourceFile *ast.SourceFile) {
|
|
p.currentSourceFile = sourceFile
|
|
p.uniqueHelperNames = nil
|
|
p.externalHelpersModuleName = nil
|
|
if sourceFile != nil {
|
|
if p.emitContext.EmitFlags(p.emitContext.MostOriginal(sourceFile.AsNode()))&EFExternalHelpers != 0 {
|
|
p.uniqueHelperNames = make(map[string]*ast.IdentifierNode)
|
|
}
|
|
p.externalHelpersModuleName = p.emitContext.GetExternalHelpersModuleName(sourceFile)
|
|
p.setSourceMapSource(sourceFile)
|
|
}
|
|
|
|
// !!!
|
|
}
|
|
|
|
func (p *Printer) Write(node *ast.Node, sourceFile *ast.SourceFile, writer EmitTextWriter, sourceMapGenerator *sourcemap.Generator) {
|
|
savedCurrentSourceFile := p.currentSourceFile
|
|
savedWriter := p.writer
|
|
savedUniqueHelperNames := p.uniqueHelperNames
|
|
savedSourceMapsDisabled := p.sourceMapsDisabled
|
|
savedSourceMapGenerator := p.sourceMapGenerator
|
|
savedSourceMapSource := p.sourceMapSource
|
|
savedSourceMapSourceIndex := p.sourceMapSourceIndex
|
|
|
|
p.sourceMapsDisabled = sourceMapGenerator == nil
|
|
p.sourceMapGenerator = sourceMapGenerator
|
|
p.sourceMapSource = nil
|
|
p.sourceMapSourceIndex = -1
|
|
|
|
p.setSourceFile(sourceFile)
|
|
p.writer = writer
|
|
p.writer.Clear()
|
|
|
|
switch node.Kind {
|
|
// Pseudo-literals
|
|
case ast.KindTemplateHead:
|
|
p.emitTemplateHead(node.AsTemplateHead())
|
|
case ast.KindTemplateMiddle:
|
|
p.emitTemplateMiddle(node.AsTemplateMiddle())
|
|
case ast.KindTemplateTail:
|
|
p.emitTemplateTail(node.AsTemplateTail())
|
|
|
|
// Identifiers
|
|
case ast.KindIdentifier:
|
|
p.emitIdentifierName(node.AsIdentifier())
|
|
|
|
// PrivateIdentifiers
|
|
case ast.KindPrivateIdentifier:
|
|
p.emitPrivateIdentifier(node.AsPrivateIdentifier())
|
|
|
|
// Parse tree nodes
|
|
// Names
|
|
case ast.KindQualifiedName:
|
|
p.emitQualifiedName(node.AsQualifiedName())
|
|
case ast.KindComputedPropertyName:
|
|
p.emitComputedPropertyName(node.AsComputedPropertyName())
|
|
|
|
// Signature elements
|
|
case ast.KindTypeParameter:
|
|
p.emitTypeParameter(node.AsTypeParameter())
|
|
case ast.KindParameter:
|
|
p.emitParameter(node.AsParameterDeclaration())
|
|
case ast.KindDecorator:
|
|
p.emitDecorator(node.AsDecorator())
|
|
|
|
// Type members
|
|
case ast.KindPropertySignature:
|
|
p.emitPropertySignature(node.AsPropertySignatureDeclaration())
|
|
case ast.KindPropertyDeclaration:
|
|
p.emitPropertyDeclaration(node.AsPropertyDeclaration())
|
|
case ast.KindMethodSignature:
|
|
p.emitMethodSignature(node.AsMethodSignatureDeclaration())
|
|
case ast.KindMethodDeclaration:
|
|
p.emitMethodDeclaration(node.AsMethodDeclaration())
|
|
case ast.KindClassStaticBlockDeclaration:
|
|
p.emitClassStaticBlockDeclaration(node.AsClassStaticBlockDeclaration())
|
|
case ast.KindConstructor:
|
|
p.emitConstructor(node.AsConstructorDeclaration())
|
|
case ast.KindGetAccessor:
|
|
p.emitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
|
|
case ast.KindSetAccessor:
|
|
p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
|
|
case ast.KindCallSignature:
|
|
p.emitCallSignature(node.AsCallSignatureDeclaration())
|
|
case ast.KindConstructSignature:
|
|
p.emitConstructSignature(node.AsConstructSignatureDeclaration())
|
|
case ast.KindIndexSignature:
|
|
p.emitIndexSignature(node.AsIndexSignatureDeclaration())
|
|
|
|
// Binding patterns
|
|
case ast.KindObjectBindingPattern:
|
|
p.emitObjectBindingPattern(node.AsBindingPattern())
|
|
case ast.KindArrayBindingPattern:
|
|
p.emitArrayBindingPattern(node.AsBindingPattern())
|
|
case ast.KindBindingElement:
|
|
p.emitBindingElement(node.AsBindingElement())
|
|
|
|
// Misc
|
|
case ast.KindTemplateSpan:
|
|
p.emitTemplateSpan(node.AsTemplateSpan())
|
|
case ast.KindSemicolonClassElement:
|
|
p.emitSemicolonClassElement(node.AsSemicolonClassElement())
|
|
|
|
// Declarations (non-statement)
|
|
case ast.KindVariableDeclaration:
|
|
p.emitVariableDeclaration(node.AsVariableDeclaration())
|
|
case ast.KindVariableDeclarationList:
|
|
p.emitVariableDeclarationList(node.AsVariableDeclarationList())
|
|
case ast.KindModuleBlock:
|
|
p.emitModuleBlock(node.AsModuleBlock())
|
|
case ast.KindCaseBlock:
|
|
p.emitCaseBlock(node.AsCaseBlock())
|
|
case ast.KindImportClause:
|
|
p.emitImportClause(node.AsImportClause())
|
|
case ast.KindNamespaceImport:
|
|
p.emitNamespaceImport(node.AsNamespaceImport())
|
|
case ast.KindNamespaceExport:
|
|
p.emitNamespaceExport(node.AsNamespaceExport())
|
|
case ast.KindNamedImports:
|
|
p.emitNamedImports(node.AsNamedImports())
|
|
case ast.KindImportSpecifier:
|
|
p.emitImportSpecifier(node.AsImportSpecifier())
|
|
case ast.KindNamedExports:
|
|
p.emitNamedExports(node.AsNamedExports())
|
|
case ast.KindExportSpecifier:
|
|
p.emitExportSpecifier(node.AsExportSpecifier())
|
|
case ast.KindImportAttributes:
|
|
p.emitImportAttributes(node.AsImportAttributes())
|
|
case ast.KindImportAttribute:
|
|
p.emitImportAttribute(node.AsImportAttribute())
|
|
|
|
// Module references
|
|
case ast.KindExternalModuleReference:
|
|
p.emitExternalModuleReference(node.AsExternalModuleReference())
|
|
|
|
// JSX (non-expression)
|
|
case ast.KindJsxText:
|
|
p.emitJsxText(node.AsJsxText())
|
|
case ast.KindJsxOpeningElement:
|
|
p.emitJsxOpeningElement(node.AsJsxOpeningElement())
|
|
case ast.KindJsxOpeningFragment:
|
|
p.emitJsxOpeningFragment(node.AsJsxOpeningFragment())
|
|
case ast.KindJsxClosingElement:
|
|
p.emitJsxClosingElement(node.AsJsxClosingElement())
|
|
case ast.KindJsxClosingFragment:
|
|
p.emitJsxClosingFragment(node.AsJsxClosingFragment())
|
|
case ast.KindJsxAttribute:
|
|
p.emitJsxAttribute(node.AsJsxAttribute())
|
|
case ast.KindJsxAttributes:
|
|
p.emitJsxAttributes(node.AsJsxAttributes())
|
|
case ast.KindJsxSpreadAttribute:
|
|
p.emitJsxSpreadAttribute(node.AsJsxSpreadAttribute())
|
|
case ast.KindJsxExpression:
|
|
p.emitJsxExpression(node.AsJsxExpression())
|
|
case ast.KindJsxNamespacedName:
|
|
p.emitJsxNamespacedName(node.AsJsxNamespacedName())
|
|
|
|
// Clauses
|
|
case ast.KindCaseClause:
|
|
p.emitCaseClause(node.AsCaseOrDefaultClause())
|
|
case ast.KindDefaultClause:
|
|
p.emitDefaultClause(node.AsCaseOrDefaultClause())
|
|
case ast.KindHeritageClause:
|
|
p.emitHeritageClause(node.AsHeritageClause())
|
|
case ast.KindCatchClause:
|
|
p.emitCatchClause(node.AsCatchClause())
|
|
|
|
// Property assignments
|
|
case ast.KindPropertyAssignment:
|
|
p.emitPropertyAssignment(node.AsPropertyAssignment())
|
|
case ast.KindShorthandPropertyAssignment:
|
|
p.emitShorthandPropertyAssignment(node.AsShorthandPropertyAssignment())
|
|
case ast.KindSpreadAssignment:
|
|
p.emitSpreadAssignment(node.AsSpreadAssignment())
|
|
|
|
// Enum
|
|
case ast.KindEnumMember:
|
|
p.emitEnumMember(node.AsEnumMember())
|
|
|
|
// Top-level nodes
|
|
case ast.KindSourceFile:
|
|
p.emitSourceFile(node.AsSourceFile())
|
|
|
|
case ast.KindBundle:
|
|
panic("not implemented")
|
|
|
|
// Transformation nodes
|
|
case ast.KindNotEmittedTypeElement:
|
|
p.emitNotEmittedTypeElement(node.AsNotEmittedTypeElement())
|
|
|
|
default:
|
|
switch {
|
|
case ast.IsTypeNode(node):
|
|
p.emitTypeNodeOutsideExtends(node)
|
|
case ast.IsStatement(node):
|
|
p.emitStatement(node)
|
|
case ast.IsExpression(node):
|
|
p.emitExpression(node, ast.OperatorPrecedenceLowest)
|
|
case ast.IsKeywordKind(node.Kind):
|
|
p.emitKeywordNode(node)
|
|
case ast.IsPunctuationKind(node.Kind):
|
|
p.emitPunctuationNode(node)
|
|
case ast.IsJSDocKind(node.Kind):
|
|
p.emitJSDocNode(node)
|
|
default:
|
|
panic(fmt.Sprintf("unhandled Node: %v", node.Kind))
|
|
}
|
|
}
|
|
|
|
p.currentSourceFile = savedCurrentSourceFile
|
|
p.writer = savedWriter
|
|
p.uniqueHelperNames = savedUniqueHelperNames
|
|
p.sourceMapsDisabled = savedSourceMapsDisabled
|
|
p.sourceMapGenerator = savedSourceMapGenerator
|
|
p.sourceMapSource = savedSourceMapSource
|
|
p.sourceMapSourceIndex = savedSourceMapSourceIndex
|
|
}
|
|
|
|
//
|
|
// Comments
|
|
//
|
|
|
|
func (p *Printer) emitCommentsBeforeNode(node *ast.Node) *commentState {
|
|
if !p.shouldEmitComments(node) {
|
|
return nil
|
|
}
|
|
|
|
emitFlags := p.emitContext.EmitFlags(node)
|
|
commentRange := p.emitContext.CommentRange(node)
|
|
containerPos := p.containerPos
|
|
containerEnd := p.containerEnd
|
|
declarationListContainerEnd := p.declarationListContainerEnd
|
|
|
|
// Emit leading comments
|
|
p.emitLeadingCommentsOfNode(node, emitFlags, commentRange)
|
|
p.emitLeadingSyntheticCommentsOfNode(node, emitFlags)
|
|
if emitFlags&EFNoNestedComments != 0 {
|
|
p.commentsDisabled = true
|
|
}
|
|
|
|
c := p.commentStatePool.New()
|
|
*c = commentState{emitFlags, commentRange, containerPos, containerEnd, declarationListContainerEnd}
|
|
return c
|
|
}
|
|
|
|
func (p *Printer) emitCommentsAfterNode(node *ast.Node, state *commentState) {
|
|
if state == nil {
|
|
return
|
|
}
|
|
|
|
emitFlags := state.emitFlags
|
|
commentRange := state.commentRange
|
|
containerPos := state.containerPos
|
|
containerEnd := state.containerEnd
|
|
declarationListContainerEnd := state.declarationListContainerEnd
|
|
|
|
// Emit trailing comments
|
|
if emitFlags&EFNoNestedComments != 0 {
|
|
p.commentsDisabled = false
|
|
}
|
|
|
|
p.emitTrailingSyntheticCommentsOfNode(node, emitFlags)
|
|
p.emitTrailingCommentsOfNode(node, emitFlags, commentRange, containerPos, containerEnd, declarationListContainerEnd)
|
|
|
|
// !!! Preserve comments from type annotation:
|
|
// typeNode := node.Type()
|
|
// if typeNode != nil {
|
|
// p.emitTrailingCommentsOfNode(node, typeNode.Pos(), typeNode.End(), state)
|
|
// }
|
|
}
|
|
|
|
func (p *Printer) emitCommentsBeforeToken(token ast.Kind, pos int, contextNode *ast.Node, flags tokenEmitFlags) (*commentState, int) {
|
|
if flags&tefNoComments != 0 {
|
|
return nil, pos
|
|
}
|
|
|
|
startPos := pos
|
|
if p.currentSourceFile != nil {
|
|
pos = scanner.SkipTrivia(p.currentSourceFile.Text(), startPos)
|
|
}
|
|
|
|
node := p.emitContext.ParseNode(contextNode)
|
|
isSimilarNode := node != nil && node.Kind == contextNode.Kind
|
|
if !isSimilarNode {
|
|
return nil, pos
|
|
}
|
|
|
|
if contextNode.Pos() != startPos {
|
|
indentLeading := flags&tefIndentLeadingComments != 0
|
|
needsIndent := indentLeading && p.currentSourceFile != nil && !positionsAreOnSameLine(startPos, pos, p.currentSourceFile)
|
|
p.increaseIndentIf(needsIndent)
|
|
p.emitLeadingComments(startPos, false /*elided*/)
|
|
p.decreaseIndentIf(needsIndent)
|
|
}
|
|
|
|
return p.commentStatePool.New(), pos
|
|
}
|
|
|
|
func (p *Printer) emitCommentsAfterToken(token ast.Kind, pos int, contextNode *ast.Node, state *commentState) {
|
|
if state == nil {
|
|
return
|
|
}
|
|
|
|
if contextNode.End() != pos {
|
|
isJsxExprContext := contextNode.Kind == ast.KindJsxExpression
|
|
p.emitTrailingComments(pos, core.IfElse(isJsxExprContext, commentSeparatorNone, commentSeparatorBefore))
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitDetachedCommentsBeforeStatementList(node *ast.Node, detachedRange core.TextRange) *commentState {
|
|
if !p.shouldEmitDetachedComments(node) {
|
|
return nil
|
|
}
|
|
|
|
emitFlags := p.emitContext.EmitFlags(node)
|
|
containerPos := p.containerPos
|
|
containerEnd := p.containerEnd
|
|
declarationListContainerEnd := p.declarationListContainerEnd
|
|
skipLeadingComments := emitFlags&EFNoLeadingComments == 0 && !ast.PositionIsSynthesized(detachedRange.Pos())
|
|
|
|
if !skipLeadingComments {
|
|
p.emitDetachedCommentsAndUpdateCommentsInfo(detachedRange)
|
|
}
|
|
|
|
if emitFlags&EFNoNestedComments != 0 {
|
|
p.commentsDisabled = true
|
|
}
|
|
|
|
return &commentState{emitFlags, detachedRange, containerPos, containerEnd, declarationListContainerEnd}
|
|
}
|
|
|
|
func (p *Printer) emitDetachedCommentsAfterStatementList(node *ast.Node, detachedRange core.TextRange, state *commentState) {
|
|
if state == nil {
|
|
return
|
|
}
|
|
|
|
emitFlags := state.emitFlags
|
|
skipTrailingComments := p.commentsDisabled || ast.PositionIsSynthesized(detachedRange.End()) || emitFlags&EFNoTrailingComments != 0
|
|
|
|
if !skipTrailingComments {
|
|
hasWrittenComment := p.emitLeadingComments(detachedRange.End(), false /*elided*/)
|
|
if hasWrittenComment && !p.writer.IsAtStartOfLine() {
|
|
p.writeLine()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitLeadingCommentsOfNode(node *ast.Node, emitFlags EmitFlags, commentRange core.TextRange) {
|
|
pos := commentRange.Pos()
|
|
end := commentRange.End()
|
|
|
|
// Save current container state on the stack.
|
|
if (!ast.PositionIsSynthesized(pos) || !ast.PositionIsSynthesized(end)) && pos != end {
|
|
// We have to explicitly check that the node is JsxText because if the compilerOptions.jsx is "preserve" we will not do any transformation.
|
|
// It is expensive to walk entire tree just to set one kind of node to have no comments.
|
|
skipLeadingComments := ast.PositionIsSynthesized(pos) || emitFlags&EFNoLeadingComments != 0 || node.Kind == ast.KindJsxText
|
|
skipTrailingComments := ast.PositionIsSynthesized(pos) || emitFlags&EFNoTrailingComments != 0 || node.Kind == ast.KindJsxText
|
|
|
|
// Emit leading comments if the position is not synthesized and the node
|
|
// has not opted out from emitting leading comments.
|
|
if !skipLeadingComments {
|
|
p.emitLeadingComments(pos, node.Kind == ast.KindNotEmittedStatement /*elided*/)
|
|
}
|
|
|
|
if !skipLeadingComments || (pos >= 0 && (emitFlags&EFNoLeadingComments) != 0) {
|
|
// Advance the container position if comments get emitted or if they've been disabled explicitly using NoLeadingComments.
|
|
p.containerPos = pos
|
|
}
|
|
|
|
if !skipTrailingComments || (end >= 0 && (emitFlags&EFNoTrailingComments) != 0) {
|
|
// Advance the container end if comments get emitted or if they've been disabled explicitly using NoTrailingComments.
|
|
p.containerEnd = end
|
|
|
|
// To avoid invalid comment emit in a down-level binding pattern, we
|
|
// keep track of the last declaration list container's end
|
|
if node.Kind == ast.KindVariableDeclarationList {
|
|
p.declarationListContainerEnd = end
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitTrailingCommentsOfNode(node *ast.Node, emitFlags EmitFlags, commentRange core.TextRange, containerPos int, containerEnd int, declarationListContainerEnd int) {
|
|
pos := commentRange.Pos()
|
|
end := commentRange.End()
|
|
skipTrailingComments := end < 0 || (emitFlags&EFNoTrailingComments) != 0 || node.Kind == ast.KindJsxText
|
|
if (!ast.PositionIsSynthesized(pos) || !ast.PositionIsSynthesized(end)) && pos != end {
|
|
// Restore previous container state.
|
|
p.containerPos = containerPos
|
|
p.containerEnd = containerEnd
|
|
p.declarationListContainerEnd = declarationListContainerEnd
|
|
|
|
// Emit trailing comments if the position is not synthesized and the node
|
|
// has not opted out from emitting leading comments and is an emitted node.
|
|
if !skipTrailingComments && node.Kind != ast.KindNotEmittedStatement {
|
|
p.emitTrailingComments(end, commentSeparatorBefore)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitLeadingSyntheticCommentsOfNode(node *ast.Node, emitFlags EmitFlags) {
|
|
if emitFlags&EFNoLeadingComments != 0 {
|
|
return
|
|
}
|
|
synth := p.emitContext.GetSyntheticLeadingComments(node)
|
|
for _, c := range synth {
|
|
p.emitLeadingSynthesizedComment(c)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitLeadingSynthesizedComment(comment SynthesizedComment) {
|
|
if comment.HasLeadingNewLine || comment.Kind == ast.KindSingleLineCommentTrivia {
|
|
p.writer.WriteLine()
|
|
}
|
|
p.writeSynthesizedComment(comment)
|
|
if comment.HasTrailingNewLine || comment.Kind == ast.KindSingleLineCommentTrivia {
|
|
p.writer.WriteLine()
|
|
} else {
|
|
p.writer.WriteSpace(" ")
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitTrailingSyntheticCommentsOfNode(node *ast.Node, emitFlags EmitFlags) {
|
|
if emitFlags&EFNoTrailingComments != 0 {
|
|
return
|
|
}
|
|
synth := p.emitContext.GetSyntheticTrailingComments(node)
|
|
for _, c := range synth {
|
|
p.emitTrailingSynthesizedComment(c)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitTrailingSynthesizedComment(comment SynthesizedComment) {
|
|
if !p.writer.IsAtStartOfLine() {
|
|
p.writer.WriteSpace(" ")
|
|
}
|
|
p.writeSynthesizedComment(comment)
|
|
if comment.HasTrailingNewLine {
|
|
p.writer.WriteLine()
|
|
}
|
|
}
|
|
|
|
func formatSynthesizedComment(comment SynthesizedComment) string {
|
|
if comment.Kind == ast.KindMultiLineCommentTrivia {
|
|
return "/*" + comment.Text + "*/"
|
|
}
|
|
return "//" + comment.Text
|
|
}
|
|
|
|
func (p *Printer) writeSynthesizedComment(comment SynthesizedComment) {
|
|
text := formatSynthesizedComment(comment)
|
|
var lineMap []core.TextPos
|
|
if comment.Kind == ast.KindMultiLineCommentTrivia {
|
|
lineMap = core.ComputeECMALineStarts(text)
|
|
}
|
|
p.writeCommentRangeWorker(text, lineMap, comment.Kind, core.NewTextRange(0, len(text)))
|
|
}
|
|
|
|
func (p *Printer) emitLeadingComments(pos int, elided bool) bool {
|
|
// Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
|
|
if p.currentSourceFile == nil || ast.PositionIsSynthesized(pos) || pos == p.containerPos {
|
|
return false
|
|
}
|
|
|
|
tripleSlash := core.TSUnknown
|
|
if !elided {
|
|
if pos == 0 && p.currentSourceFile != nil && p.currentSourceFile.IsDeclarationFile {
|
|
tripleSlash = core.TSFalse
|
|
}
|
|
} else if pos == 0 {
|
|
// If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
|
|
// unless it is a triple slash comment at the top of the file.
|
|
// For Example:
|
|
// /// <reference-path ...>
|
|
// declare var x;
|
|
// /// <reference-path ...>
|
|
// interface F {}
|
|
// The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
|
|
tripleSlash = core.TSTrue
|
|
} else {
|
|
return false
|
|
}
|
|
|
|
// skip detached comments
|
|
if p.detachedCommentsInfo.Len() > 0 {
|
|
if info := p.detachedCommentsInfo.Peek(); info.nodePos == pos {
|
|
pos = p.detachedCommentsInfo.Pop().detachedCommentEndPos
|
|
}
|
|
}
|
|
|
|
var comments []ast.CommentRange
|
|
for comment := range scanner.GetLeadingCommentRanges(p.emitContext.Factory.AsNodeFactory(), p.currentSourceFile.Text(), pos) {
|
|
if p.shouldWriteComment(comment) && p.shouldEmitCommentIfTripleSlash(comment, tripleSlash) {
|
|
comments = append(comments, comment)
|
|
}
|
|
}
|
|
|
|
if len(comments) > 0 && p.shouldEmitNewLineBeforeLeadingCommentOfPosition(pos, comments[0].Pos()) {
|
|
p.writeLine()
|
|
}
|
|
|
|
// Leading comments are emitted as /*leading comment1*/space/*leading comment*/space
|
|
return p.emitComments(comments, commentSeparatorAfter)
|
|
}
|
|
|
|
func (p *Printer) shouldEmitCommentIfTripleSlash(comment ast.CommentRange, tripleSlash core.Tristate) bool {
|
|
switch tripleSlash {
|
|
case core.TSTrue:
|
|
return p.isTripleSlashComment(comment)
|
|
case core.TSFalse:
|
|
return !p.isTripleSlashComment(comment)
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
func (p *Printer) shouldEmitNewLineBeforeLeadingCommentOfPosition(pos int, commentPos int) bool {
|
|
// If the leading comments start on different line than the start of node, write new line
|
|
return p.currentSourceFile != nil &&
|
|
pos != commentPos &&
|
|
scanner.ComputeLineOfPosition(p.currentSourceFile.ECMALineMap(), pos) != scanner.ComputeLineOfPosition(p.currentSourceFile.ECMALineMap(), commentPos)
|
|
}
|
|
|
|
func (p *Printer) emitTrailingComments(pos int, commentSeparator commentSeparator) {
|
|
// Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
|
|
if p.currentSourceFile == nil || p.containerEnd != -1 && (pos == p.containerEnd || pos == p.declarationListContainerEnd) {
|
|
return
|
|
}
|
|
|
|
var comments []ast.CommentRange
|
|
for comment := range scanner.GetTrailingCommentRanges(p.emitContext.Factory.AsNodeFactory(), p.currentSourceFile.Text(), pos) {
|
|
if p.shouldWriteComment(comment) {
|
|
comments = append(comments, comment)
|
|
}
|
|
}
|
|
|
|
// trailing comments are normally emitted as space/*trailing comment1*/space/*trailing comment2*/
|
|
p.emitComments(comments, commentSeparator)
|
|
}
|
|
|
|
func (p *Printer) emitDetachedCommentsAndUpdateCommentsInfo(textRange core.TextRange) {
|
|
if p.currentSourceFile == nil {
|
|
return
|
|
}
|
|
if currentDetachedCommentInfo, ok := p.emitDetachedComments(textRange); ok {
|
|
p.detachedCommentsInfo.Push(currentDetachedCommentInfo)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitDetachedComments(textRange core.TextRange) (result detachedCommentsInfo, hasResult bool) {
|
|
if p.currentSourceFile == nil {
|
|
return result, hasResult
|
|
}
|
|
|
|
text := p.currentSourceFile.Text()
|
|
lineMap := p.currentSourceFile.ECMALineMap()
|
|
|
|
var leadingComments []ast.CommentRange
|
|
if p.commentsDisabled {
|
|
// removeComments is true, only reserve pinned comment at the top of file
|
|
// For example:
|
|
// /*! Pinned Comment */
|
|
//
|
|
// var x = 10;
|
|
if textRange.Pos() == 0 {
|
|
for comment := range scanner.GetLeadingCommentRanges(p.emitContext.Factory.AsNodeFactory(), text, textRange.Pos()) {
|
|
if IsPinnedComment(text, comment) {
|
|
leadingComments = append(leadingComments, comment)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// removeComments is false, just get detached as normal and bypass the process to filter comment
|
|
leadingComments = slices.Collect(scanner.GetLeadingCommentRanges(p.emitContext.Factory.AsNodeFactory(), text, textRange.Pos()))
|
|
}
|
|
|
|
if len(leadingComments) > 0 {
|
|
var detachedComments []ast.CommentRange
|
|
var lastComment ast.CommentRange
|
|
for i, comment := range leadingComments {
|
|
if i > 0 {
|
|
lastCommentLine := scanner.ComputeLineOfPosition(lineMap, lastComment.End())
|
|
commentLine := scanner.ComputeLineOfPosition(lineMap, comment.Pos())
|
|
|
|
if commentLine >= lastCommentLine+2 {
|
|
// There was a blank line between the last comment and this comment. This
|
|
// comment is not part of the copyright comments. Return what we have so
|
|
// far.
|
|
break
|
|
}
|
|
}
|
|
|
|
if p.shouldWriteComment(comment) {
|
|
detachedComments = append(detachedComments, comment)
|
|
}
|
|
|
|
lastComment = comment
|
|
}
|
|
|
|
if len(detachedComments) > 0 {
|
|
// All comments look like they could have been part of the copyright header. Make
|
|
// sure there is at least one blank line between it and the node. If not, it's not
|
|
// a copyright header.
|
|
lastCommentLine := scanner.ComputeLineOfPosition(lineMap, core.LastOrNil(detachedComments).End())
|
|
nodeLine := scanner.ComputeLineOfPosition(lineMap, scanner.SkipTrivia(text, textRange.Pos()))
|
|
if nodeLine >= lastCommentLine+2 {
|
|
// Valid detachedComments
|
|
|
|
if len(leadingComments) > 0 && p.shouldEmitNewLineBeforeLeadingCommentOfPosition(textRange.Pos(), leadingComments[0].Pos()) {
|
|
p.writeLine()
|
|
}
|
|
|
|
p.emitComments(detachedComments, commentSeparatorAfter)
|
|
result = detachedCommentsInfo{nodePos: textRange.Pos(), detachedCommentEndPos: core.LastOrNil(detachedComments).End()}
|
|
hasResult = true
|
|
}
|
|
}
|
|
}
|
|
return result, hasResult
|
|
}
|
|
|
|
type commentSeparator uint32
|
|
|
|
const (
|
|
commentSeparatorNone commentSeparator = iota
|
|
commentSeparatorBefore
|
|
commentSeparatorAfter
|
|
)
|
|
|
|
func (p *Printer) emitComments(comments []ast.CommentRange, commentSeparator commentSeparator) bool {
|
|
interveningSeparator := false
|
|
if len(comments) == 0 {
|
|
return false
|
|
}
|
|
|
|
if commentSeparator == commentSeparatorBefore && !p.writer.IsAtStartOfLine() {
|
|
p.writeSpace()
|
|
}
|
|
|
|
for _, comment := range comments {
|
|
if interveningSeparator {
|
|
p.writeSpace()
|
|
interveningSeparator = false
|
|
}
|
|
|
|
p.emitComment(comment)
|
|
|
|
if comment.Kind == ast.KindSingleLineCommentTrivia || comment.HasTrailingNewLine && commentSeparator != commentSeparatorNone {
|
|
p.writeLine()
|
|
} else {
|
|
interveningSeparator = commentSeparator != commentSeparatorNone
|
|
}
|
|
}
|
|
|
|
if interveningSeparator && commentSeparator == commentSeparatorAfter && !p.writer.IsAtStartOfLine() {
|
|
p.writeSpace()
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *Printer) emitComment(comment ast.CommentRange) {
|
|
p.emitPos(comment.Pos())
|
|
p.writeCommentRange(comment)
|
|
p.emitPos(comment.End())
|
|
}
|
|
|
|
func (p *Printer) isTripleSlashComment(comment ast.CommentRange) bool {
|
|
return p.currentSourceFile != nil &&
|
|
IsRecognizedTripleSlashComment(p.currentSourceFile.Text(), comment)
|
|
}
|
|
|
|
//
|
|
// Source Maps
|
|
//
|
|
|
|
func (p *Printer) setSourceMapSource(source sourcemap.Source) {
|
|
if p.sourceMapsDisabled {
|
|
return
|
|
}
|
|
|
|
p.sourceMapSource = source
|
|
if p.mostRecentSourceMapSource == source {
|
|
p.sourceMapSourceIndex = p.mostRecentSourceMapSourceIndex
|
|
return
|
|
}
|
|
|
|
p.sourceMapSourceIsJson = tspath.FileExtensionIs(source.FileName(), tspath.ExtensionJson)
|
|
if p.sourceMapSourceIsJson {
|
|
return
|
|
}
|
|
|
|
p.sourceMapSourceIndex = p.sourceMapGenerator.AddSource(source.FileName())
|
|
if p.Options.InlineSources {
|
|
if err := p.sourceMapGenerator.SetSourceContent(p.sourceMapSourceIndex, source.Text()); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
p.mostRecentSourceMapSource = source
|
|
p.mostRecentSourceMapSourceIndex = p.sourceMapSourceIndex
|
|
}
|
|
|
|
func (p *Printer) emitPos(pos int) {
|
|
if p.sourceMapsDisabled || p.sourceMapSource == nil || p.sourceMapGenerator == nil || p.sourceMapSourceIsJson || ast.PositionIsSynthesized(pos) {
|
|
return
|
|
}
|
|
|
|
sourceLine, sourceCharacter := scanner.GetECMALineAndCharacterOfPosition(p.sourceMapSource, pos)
|
|
if err := p.sourceMapGenerator.AddSourceMapping(
|
|
p.writer.GetLine(),
|
|
p.writer.GetColumn(),
|
|
p.sourceMapSourceIndex,
|
|
sourceLine,
|
|
sourceCharacter,
|
|
); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// TODO: Support emitting nameIndex for source maps
|
|
////func (p *Printer) emitPosName(pos int, name string) {
|
|
//// if p.sourceMapsDisabled || p.sourceMapSource == nil || p.sourceMapGenerator == nil || p.sourceMapSourceIsJson || ast.PositionIsSynthesized(pos) {
|
|
//// return
|
|
//// }
|
|
////
|
|
//// sourceLine, sourceCharacter := scanner.GetLineAndCharacterOfPosition(p.sourceMapSource, pos)
|
|
//// nameIndex := p.sourceMapGenerator.AddName(name)
|
|
//// if err := p.sourceMapGenerator.AddNamedSourceMapping(
|
|
//// p.writer.GetLine(),
|
|
//// p.writer.GetColumn(),
|
|
//// p.sourceMapSourceIndex,
|
|
//// sourceLine,
|
|
//// sourceCharacter,
|
|
//// nameIndex,
|
|
//// ); err != nil {
|
|
//// panic(err)
|
|
//// }
|
|
////}
|
|
|
|
func (p *Printer) emitSourcePos(source sourcemap.Source, pos int) {
|
|
if source != p.sourceMapSource {
|
|
savedSourceMapSource := p.sourceMapSource
|
|
savedSourceMapSourceIndex := p.sourceMapSourceIndex
|
|
p.setSourceMapSource(source)
|
|
p.emitPos(pos)
|
|
p.sourceMapSource = savedSourceMapSource
|
|
p.sourceMapSourceIndex = savedSourceMapSourceIndex
|
|
} else {
|
|
p.emitPos(pos)
|
|
}
|
|
}
|
|
|
|
// TODO: Support emitting nameIndex for source maps
|
|
////func (p *Printer) emitSourcePosName(source sourcemap.Source, pos int, name string) {
|
|
//// if source != p.sourceMapSource {
|
|
//// savedSourceMapSource := p.sourceMapSource
|
|
//// savedSourceMapSourceIndex := p.sourceMapSourceIndex
|
|
//// p.setSourceMapSource(source)
|
|
//// p.emitPosName(pos, name)
|
|
//// p.sourceMapSource = savedSourceMapSource
|
|
//// p.sourceMapSourceIndex = savedSourceMapSourceIndex
|
|
//// } else {
|
|
//// p.emitPosName(pos, name)
|
|
//// }
|
|
////}
|
|
|
|
func (p *Printer) emitSourceMapsBeforeNode(node *ast.Node) *sourceMapState {
|
|
if !p.shouldEmitSourceMaps(node) {
|
|
return nil
|
|
}
|
|
|
|
emitFlags := p.emitContext.EmitFlags(node)
|
|
loc := p.emitContext.SourceMapRange(node)
|
|
|
|
if !ast.IsNotEmittedStatement(node) &&
|
|
emitFlags&EFNoLeadingSourceMap == 0 &&
|
|
!ast.PositionIsSynthesized(loc.Pos()) {
|
|
p.emitSourcePos(p.sourceMapSource, scanner.SkipTrivia(p.currentSourceFile.Text(), loc.Pos())) // !!! support SourceMapRange from Strada?
|
|
}
|
|
|
|
if emitFlags&EFNoNestedSourceMaps != 0 {
|
|
p.sourceMapsDisabled = true
|
|
}
|
|
|
|
state := p.sourceMapStatePool.New()
|
|
*state = sourceMapState{emitFlags, loc, false}
|
|
return state
|
|
}
|
|
|
|
func (p *Printer) emitSourceMapsAfterNode(node *ast.Node, previousState *sourceMapState) {
|
|
if previousState == nil {
|
|
return
|
|
}
|
|
|
|
emitFlags := previousState.emitFlags
|
|
loc := previousState.sourceMapRange
|
|
|
|
if emitFlags&EFNoNestedSourceMaps != 0 {
|
|
p.sourceMapsDisabled = false
|
|
}
|
|
|
|
if !ast.IsNotEmittedStatement(node) &&
|
|
emitFlags&EFNoTrailingSourceMap == 0 &&
|
|
!ast.PositionIsSynthesized(loc.End()) {
|
|
p.emitSourcePos(p.sourceMapSource, loc.End()) // !!! support SourceMapRange from Strada?
|
|
}
|
|
}
|
|
|
|
func (p *Printer) emitSourceMapsBeforeToken(token ast.Kind, pos int, contextNode *ast.Node, flags tokenEmitFlags) *sourceMapState {
|
|
if !p.shouldEmitTokenSourceMaps(token, pos, contextNode, flags) {
|
|
return nil
|
|
}
|
|
|
|
emitFlags := p.emitContext.EmitFlags(contextNode)
|
|
loc, hasLoc := p.emitContext.TokenSourceMapRange(contextNode, token)
|
|
if emitFlags&EFNoTokenLeadingSourceMaps == 0 {
|
|
if hasLoc {
|
|
pos = loc.Pos()
|
|
}
|
|
if pos >= 0 {
|
|
p.emitSourcePos(p.sourceMapSource, pos) // !!! support SourceMapRange from Strada?
|
|
}
|
|
}
|
|
|
|
state := p.sourceMapStatePool.New()
|
|
*state = sourceMapState{emitFlags, loc, hasLoc}
|
|
return state
|
|
}
|
|
|
|
func (p *Printer) emitSourceMapsAfterToken(token ast.Kind, pos int, contextNode *ast.Node, previousState *sourceMapState) {
|
|
if previousState == nil {
|
|
return
|
|
}
|
|
|
|
emitFlags := previousState.emitFlags
|
|
loc := previousState.sourceMapRange
|
|
hasLoc := previousState.hasTokenSourceMapRange
|
|
if emitFlags&EFNoTokenTrailingSourceMaps == 0 {
|
|
if hasLoc {
|
|
pos = loc.End()
|
|
}
|
|
if pos >= 0 {
|
|
p.emitSourcePos(p.sourceMapSource, pos) // !!! support SourceMapRange from Strada?
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Name Generation
|
|
//
|
|
|
|
func (p *Printer) shouldReuseTempVariableScope(node *ast.Node) bool {
|
|
return node != nil && p.emitContext.EmitFlags(node)&EFReuseTempVariableScope != 0
|
|
}
|
|
|
|
func (p *Printer) pushNameGenerationScope(node *ast.Node) {
|
|
p.nameGenerator.PushScope(p.shouldReuseTempVariableScope(node))
|
|
}
|
|
|
|
func (p *Printer) popNameGenerationScope(node *ast.Node) {
|
|
p.nameGenerator.PopScope(p.shouldReuseTempVariableScope(node))
|
|
}
|
|
|
|
func (p *Printer) generateAllNames(nodes *ast.NodeList) {
|
|
if nodes == nil {
|
|
return
|
|
}
|
|
for _, node := range nodes.Nodes {
|
|
p.generateNames(node)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) generateNames(node *ast.Node) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindBlock:
|
|
p.generateAllNames(node.AsBlock().Statements)
|
|
case ast.KindLabeledStatement:
|
|
p.generateNames(node.AsLabeledStatement().Statement)
|
|
case ast.KindWithStatement:
|
|
p.generateNames(node.AsWithStatement().Statement)
|
|
case ast.KindDoStatement:
|
|
p.generateNames(node.AsDoStatement().Statement)
|
|
case ast.KindWhileStatement:
|
|
p.generateNames(node.AsWhileStatement().Statement)
|
|
case ast.KindIfStatement:
|
|
p.generateNames(node.AsIfStatement().ThenStatement)
|
|
p.generateNames(node.AsIfStatement().ElseStatement)
|
|
case ast.KindForStatement:
|
|
p.generateNames(node.AsForStatement().Initializer)
|
|
p.generateNames(node.AsForStatement().Statement)
|
|
case ast.KindForOfStatement, ast.KindForInStatement:
|
|
p.generateNames(node.AsForInOrOfStatement().Initializer)
|
|
p.generateNames(node.AsForInOrOfStatement().Statement)
|
|
case ast.KindSwitchStatement:
|
|
p.generateNames(node.AsSwitchStatement().CaseBlock)
|
|
case ast.KindCaseBlock:
|
|
p.generateAllNames(node.AsCaseBlock().Clauses)
|
|
case ast.KindCaseClause, ast.KindDefaultClause:
|
|
p.generateAllNames(node.AsCaseOrDefaultClause().Statements)
|
|
case ast.KindTryStatement:
|
|
p.generateNames(node.AsTryStatement().TryBlock)
|
|
p.generateNames(node.AsTryStatement().CatchClause)
|
|
p.generateNames(node.AsTryStatement().FinallyBlock)
|
|
case ast.KindCatchClause:
|
|
p.generateNames(node.AsCatchClause().VariableDeclaration)
|
|
p.generateNames(node.AsCatchClause().Block)
|
|
case ast.KindVariableStatement:
|
|
p.generateNames(node.AsVariableStatement().DeclarationList)
|
|
case ast.KindVariableDeclarationList:
|
|
p.generateAllNames(node.AsVariableDeclarationList().Declarations)
|
|
case ast.KindVariableDeclaration, ast.KindParameter, ast.KindBindingElement, ast.KindClassDeclaration:
|
|
p.generateNameIfNeeded(node.Name())
|
|
case ast.KindFunctionDeclaration:
|
|
p.generateNameIfNeeded(node.Name())
|
|
if p.shouldReuseTempVariableScope(node) {
|
|
p.generateAllNames(node.AsFunctionDeclaration().Parameters)
|
|
p.generateNames(node.AsFunctionDeclaration().Body)
|
|
}
|
|
case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern:
|
|
p.generateAllNames(node.AsBindingPattern().Elements)
|
|
case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
|
|
p.generateNames(node.AsImportDeclaration().ImportClause)
|
|
case ast.KindImportClause:
|
|
p.generateNameIfNeeded(node.AsImportClause().Name())
|
|
p.generateNames(node.AsImportClause().NamedBindings)
|
|
case ast.KindNamespaceImport, ast.KindNamespaceExport:
|
|
p.generateNameIfNeeded(node.Name())
|
|
case ast.KindNamedImports:
|
|
p.generateAllNames(node.AsNamedImports().Elements)
|
|
case ast.KindImportSpecifier:
|
|
n := node.AsImportSpecifier()
|
|
if n.PropertyName != nil {
|
|
p.generateNameIfNeeded(n.PropertyName)
|
|
} else {
|
|
p.generateNameIfNeeded(n.Name())
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Printer) generateAllMemberNames(nodes *ast.NodeList) {
|
|
if nodes == nil {
|
|
return
|
|
}
|
|
for _, node := range nodes.Nodes {
|
|
p.generateMemberNames(node)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) generateMemberNames(node *ast.Node) {
|
|
if node == nil {
|
|
return
|
|
}
|
|
switch node.Kind {
|
|
case ast.KindPropertyAssignment,
|
|
ast.KindShorthandPropertyAssignment,
|
|
ast.KindPropertyDeclaration,
|
|
ast.KindPropertySignature,
|
|
ast.KindMethodDeclaration,
|
|
ast.KindMethodSignature,
|
|
ast.KindGetAccessor,
|
|
ast.KindSetAccessor:
|
|
p.generateNameIfNeeded(node.Name())
|
|
}
|
|
}
|
|
|
|
func (p *Printer) generateNameIfNeeded(name *ast.DeclarationName) {
|
|
if name != nil {
|
|
if ast.IsMemberName(name) {
|
|
p.generateName(name)
|
|
} else if ast.IsBindingPattern(name) {
|
|
p.generateNames(name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate the text for a generated identifier or private identifier
|
|
func (p *Printer) generateName(name *ast.MemberName) {
|
|
_ = p.nameGenerator.GenerateName(name)
|
|
}
|
|
|
|
// Returns a value indicating whether a name is unique globally or within the current file.
|
|
func (p *Printer) isFileLevelUniqueNameInCurrentFile(name string, _ bool) bool {
|
|
if p.currentSourceFile != nil {
|
|
return IsFileLevelUniqueName(p.currentSourceFile, name, p.HasGlobalName)
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
//
|
|
// Scoped operations
|
|
//
|
|
|
|
func (p *Printer) enterNode(node *ast.Node) printerState {
|
|
state := printerState{}
|
|
|
|
if p.OnBeforeEmitNode != nil {
|
|
p.OnBeforeEmitNode(node)
|
|
}
|
|
|
|
state.commentState = p.emitCommentsBeforeNode(node)
|
|
state.sourceMapState = p.emitSourceMapsBeforeNode(node)
|
|
return state
|
|
}
|
|
|
|
func (p *Printer) exitNode(node *ast.Node, previousState printerState) {
|
|
p.emitSourceMapsAfterNode(node, previousState.sourceMapState)
|
|
p.emitCommentsAfterNode(node, previousState.commentState)
|
|
|
|
if p.OnAfterEmitNode != nil {
|
|
p.OnAfterEmitNode(node)
|
|
}
|
|
}
|
|
|
|
func (p *Printer) enterTokenNode(node *ast.Node, flags tokenEmitFlags) printerState {
|
|
state := printerState{}
|
|
|
|
if p.OnBeforeEmitToken != nil {
|
|
p.OnBeforeEmitToken(node)
|
|
}
|
|
|
|
if flags&tefNoComments == 0 {
|
|
state.commentState = p.emitCommentsBeforeNode(node)
|
|
}
|
|
if flags&tefNoSourceMaps == 0 {
|
|
state.sourceMapState = p.emitSourceMapsBeforeNode(node)
|
|
}
|
|
return state
|
|
}
|
|
|
|
func (p *Printer) exitTokenNode(node *ast.Node, previousState printerState) {
|
|
p.emitSourceMapsAfterNode(node, previousState.sourceMapState)
|
|
p.emitCommentsAfterNode(node, previousState.commentState)
|
|
|
|
if p.OnAfterEmitToken != nil {
|
|
p.OnAfterEmitToken(node)
|
|
}
|
|
}
|
|
|
|
type tokenEmitFlags uint32
|
|
|
|
const (
|
|
tefNoComments tokenEmitFlags = 1 << iota
|
|
tefIndentLeadingComments
|
|
tefNoSourceMaps
|
|
|
|
tefNone tokenEmitFlags = 0
|
|
)
|
|
|
|
func (p *Printer) enterToken(token ast.Kind, pos int, contextNode *ast.Node, flags tokenEmitFlags) (printerState, int) {
|
|
state := printerState{}
|
|
state.commentState, pos = p.emitCommentsBeforeToken(token, pos, contextNode, flags)
|
|
state.sourceMapState = p.emitSourceMapsBeforeToken(token, pos, contextNode, flags)
|
|
return state, pos
|
|
}
|
|
|
|
func (p *Printer) exitToken(token ast.Kind, pos int, contextNode *ast.Node, previousState printerState) {
|
|
p.emitSourceMapsAfterToken(token, pos, contextNode, previousState.sourceMapState)
|
|
p.emitCommentsAfterToken(token, pos, contextNode, previousState.commentState)
|
|
}
|
|
|
|
type ListFormat int
|
|
|
|
const (
|
|
LFNone ListFormat = 0
|
|
|
|
// Line separators
|
|
LFSingleLine ListFormat = 0 // Prints the list on a single line (default).
|
|
LFMultiLine ListFormat = 1 << 0 // Prints the list on multiple lines.
|
|
LFPreserveLines ListFormat = 1 << 1 // Prints the list using line preservation if possible.
|
|
LFLinesMask ListFormat = LFSingleLine | LFMultiLine | LFPreserveLines
|
|
|
|
// Delimiters
|
|
LFNotDelimited ListFormat = 0 // There is no delimiter between list items (default).
|
|
LFBarDelimited ListFormat = 1 << 2 // Each list item is space-and-bar (" |") delimited.
|
|
LFAmpersandDelimited ListFormat = 1 << 3 // Each list item is space-and-ampersand (" &") delimited.
|
|
LFCommaDelimited ListFormat = 1 << 4 // Each list item is comma (",") delimited.
|
|
LFAsteriskDelimited ListFormat = 1 << 5 // Each list item is asterisk ("\n *") delimited, used with JSDoc.
|
|
LFDelimitersMask ListFormat = LFBarDelimited | LFAmpersandDelimited | LFCommaDelimited | LFAsteriskDelimited
|
|
|
|
LFAllowTrailingComma ListFormat = 1 << 6 // Write a trailing comma (",") if present.
|
|
|
|
// Whitespace
|
|
LFIndented ListFormat = 1 << 7 // The list should be indented.
|
|
LFSpaceBetweenBraces ListFormat = 1 << 8 // Inserts a space after the opening brace and before the closing brace.
|
|
LFSpaceBetweenSiblings ListFormat = 1 << 9 // Inserts a space between each sibling node.
|
|
|
|
// Brackets/Braces
|
|
LFBraces ListFormat = 1 << 10 // The list is surrounded by "{" and "}".
|
|
LFParenthesis ListFormat = 1 << 11 // The list is surrounded by "(" and ")".
|
|
LFAngleBrackets ListFormat = 1 << 12 // The list is surrounded by "<" and ">".
|
|
LFSquareBrackets ListFormat = 1 << 13 // The list is surrounded by "[" and "]".
|
|
LFBracketsMask ListFormat = LFBraces | LFParenthesis | LFAngleBrackets | LFSquareBrackets
|
|
|
|
LFOptionalIfNil ListFormat = 1 << 14 // Do not emit brackets if the list is nil.
|
|
LFOptionalIfEmpty ListFormat = 1 << 15 // Do not emit brackets if the list is empty.
|
|
LFOptional ListFormat = LFOptionalIfNil | LFOptionalIfEmpty
|
|
|
|
// Other
|
|
LFPreferNewLine ListFormat = 1 << 16 // Prefer adding a LineTerminator between synthesized nodes.
|
|
LFNoTrailingNewLine ListFormat = 1 << 17 // Do not emit a trailing NewLine for a MultiLine list.
|
|
LFNoInterveningComments ListFormat = 1 << 18 // Do not emit comments between each node
|
|
LFNoSpaceIfEmpty ListFormat = 1 << 19 // If the literal is empty, do not add spaces between braces.
|
|
LFSingleElement ListFormat = 1 << 20
|
|
LFSpaceAfterList ListFormat = 1 << 21 // Add space after list
|
|
|
|
// Precomputed Formats
|
|
LFModifiers ListFormat = LFSingleLine | LFSpaceBetweenSiblings | LFNoInterveningComments | LFSpaceAfterList
|
|
LFHeritageClauses ListFormat = LFSingleLine | LFSpaceBetweenSiblings
|
|
LFSingleLineTypeLiteralMembers ListFormat = LFSingleLine | LFSpaceBetweenBraces | LFSpaceBetweenSiblings
|
|
LFMultiLineTypeLiteralMembers ListFormat = LFMultiLine | LFIndented | LFOptionalIfEmpty
|
|
|
|
LFSingleLineTupleTypeElements ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
|
|
LFMultiLineTupleTypeElements ListFormat = LFCommaDelimited | LFIndented | LFSpaceBetweenSiblings | LFMultiLine
|
|
LFUnionTypeConstituents ListFormat = LFBarDelimited | LFSpaceBetweenSiblings | LFSingleLine
|
|
LFIntersectionTypeConstituents ListFormat = LFAmpersandDelimited | LFSpaceBetweenSiblings | LFSingleLine
|
|
LFObjectBindingPatternElements ListFormat = LFSingleLine | LFAllowTrailingComma | LFSpaceBetweenBraces | LFCommaDelimited | LFSpaceBetweenSiblings | LFNoSpaceIfEmpty
|
|
LFArrayBindingPatternElements ListFormat = LFSingleLine | LFAllowTrailingComma | LFCommaDelimited | LFSpaceBetweenSiblings | LFNoSpaceIfEmpty
|
|
LFObjectLiteralExpressionProperties ListFormat = LFPreserveLines | LFCommaDelimited | LFSpaceBetweenSiblings | LFSpaceBetweenBraces | LFIndented | LFBraces | LFNoSpaceIfEmpty
|
|
LFImportAttributes ListFormat = LFPreserveLines | LFCommaDelimited | LFSpaceBetweenSiblings | LFSpaceBetweenBraces | LFIndented | LFBraces | LFNoSpaceIfEmpty
|
|
LFArrayLiteralExpressionElements ListFormat = LFPreserveLines | LFCommaDelimited | LFSpaceBetweenSiblings | LFAllowTrailingComma | LFIndented | LFSquareBrackets
|
|
LFCommaListElements ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
|
|
LFCallExpressionArguments ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFParenthesis
|
|
LFNewExpressionArguments ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFParenthesis | LFOptionalIfNil
|
|
LFTemplateExpressionSpans ListFormat = LFSingleLine | LFNoInterveningComments
|
|
LFSingleLineBlockStatements ListFormat = LFSpaceBetweenBraces | LFSpaceBetweenSiblings | LFSingleLine
|
|
LFMultiLineBlockStatements ListFormat = LFIndented | LFMultiLine
|
|
LFVariableDeclarationList ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
|
|
LFSingleLineFunctionBodyStatements ListFormat = LFSingleLine | LFSpaceBetweenSiblings | LFSpaceBetweenBraces
|
|
LFMultiLineFunctionBodyStatements ListFormat = LFMultiLine
|
|
LFClassHeritageClauses ListFormat = LFSingleLine
|
|
LFClassMembers ListFormat = LFIndented | LFMultiLine
|
|
LFInterfaceMembers ListFormat = LFIndented | LFMultiLine
|
|
LFEnumMembers ListFormat = LFCommaDelimited | LFIndented | LFMultiLine
|
|
LFCaseBlockClauses ListFormat = LFIndented | LFMultiLine
|
|
LFNamedImportsOrExportsElements ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFAllowTrailingComma | LFSingleLine | LFSpaceBetweenBraces | LFNoSpaceIfEmpty
|
|
LFJsxElementOrFragmentChildren ListFormat = LFSingleLine | LFNoInterveningComments
|
|
LFJsxElementAttributes ListFormat = LFSingleLine | LFSpaceBetweenSiblings | LFNoInterveningComments
|
|
LFCaseOrDefaultClauseStatements ListFormat = LFIndented | LFMultiLine | LFNoTrailingNewLine | LFOptionalIfEmpty
|
|
LFHeritageClauseTypes ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
|
|
LFSourceFileStatements ListFormat = LFMultiLine | LFNoTrailingNewLine
|
|
LFDecorators ListFormat = LFMultiLine | LFOptional | LFSpaceAfterList
|
|
LFTypeArguments ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFAngleBrackets | LFOptional
|
|
LFTypeParameters ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFAngleBrackets | LFOptional
|
|
LFParameters ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFParenthesis
|
|
LFSingleArrowParameter ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine
|
|
LFIndexSignatureParameters ListFormat = LFCommaDelimited | LFSpaceBetweenSiblings | LFSingleLine | LFIndented | LFSquareBrackets
|
|
LFJSDocComment ListFormat = LFMultiLine | LFAsteriskDelimited
|
|
LFImportClauseEntries ListFormat = LFImportAttributes // Deprecated: Use LFImportAttributes
|
|
)
|
|
|
|
func getOpeningBracket(format ListFormat) string {
|
|
switch format & LFBracketsMask {
|
|
case LFBraces:
|
|
return "{"
|
|
case LFParenthesis:
|
|
return "("
|
|
case LFAngleBrackets:
|
|
return "<"
|
|
case LFSquareBrackets:
|
|
return "["
|
|
default:
|
|
panic(fmt.Sprintf("Unexpected bracket: %v", format&LFBracketsMask))
|
|
}
|
|
}
|
|
|
|
func getClosingBracket(format ListFormat) string {
|
|
switch format & LFBracketsMask {
|
|
case LFBraces:
|
|
return "}"
|
|
case LFParenthesis:
|
|
return ")"
|
|
case LFAngleBrackets:
|
|
return ">"
|
|
case LFSquareBrackets:
|
|
return "]"
|
|
default:
|
|
panic(fmt.Sprintf("Unexpected bracket: %v", format&LFBracketsMask))
|
|
}
|
|
}
|