-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Fully deprecate the symbol display builder, reimplement in terms of node builder #18860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fully deprecate the symbol display builder, reimplement in terms of node builder #18860
Conversation
ec3f036
to
f5c79d5
Compare
src/compiler/checker.ts
Outdated
return result; | ||
} | ||
if (flags & TypeFormatFlags.NoTruncation) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we align the values between these two enums and just use a bitmask?
src/compiler/emitter.ts
Outdated
@@ -286,16 +286,21 @@ namespace ts { | |||
let tempFlags: TempFlags; // TempFlags for the current name generation scope. | |||
let writer: EmitTextWriter; | |||
let ownWriter: EmitTextWriter; | |||
let write = writeBase; | |||
let pendingSemicolon = false; | |||
const syntheticParent: TextRange = { pos: -1, end: -1 }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly adds polymorphism because Node
sets id
and kind
before pos
and end
, resulting in an incompatible hidden class. Consider moving pos
and end
up in Node
, or give syntheticParent
an id
and a kind
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved pos
and end
before the other fields - they were already first in the services constructor implementation for Node
, so I think this only changes tsc
.
src/compiler/emitter.ts
Outdated
@@ -887,8 +911,8 @@ namespace ts { | |||
// | |||
|
|||
function emitIdentifier(node: Identifier) { | |||
write(getTextOfNode(node, /*includeTrivia*/ false)); | |||
emitTypeArguments(node, node.typeArguments); | |||
(node.symbol ? writeSymbol : write)(getTextOfNode(node, /*includeTrivia*/ false), node.symbol); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ternary makes this harder to read.
src/compiler/emitter.ts
Outdated
} | ||
|
||
function emitParameter(node: ParameterDeclaration) { | ||
emitDecorators(node, node.decorators); | ||
emitModifiers(node, node.modifiers); | ||
emitIfPresent(node.dotDotDotToken); | ||
if (node.name) { | ||
const savedWrite = write; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You use this pattern in enough places to consider a helper to do this: emitWithWriter(node.name, writeParameter)
function emitNodeWithWriter(node: Node, writer: typeof write) {
const savedWrite = write;
write = writer;
emit(node);
write = savedWrite;
}
src/compiler/emitter.ts
Outdated
emit(node.name); | ||
emitTypeParameters(node, node.typeParameters); | ||
write(" = "); | ||
writeSpace(); writePunctuation("="); writeSpace(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separate lines
src/services/signatureHelp.ts
Outdated
typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation)); | ||
const typeParameterParts = mapToDisplayParts(writer => { | ||
if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) { | ||
const printer = createPrinter({ removeComments: true }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, you only need one printer
.
src/services/signatureHelp.ts
Outdated
const displayParts = mapToDisplayParts(writer => | ||
typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(typeParameter, writer, invocation)); | ||
const displayParts = mapToDisplayParts(writer => { | ||
const printer = createPrinter({ removeComments: true }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, Printer
is designed to be reused when passed the same set of initial arguments. No sense in recreating it all the time.
src/services/utilities.ts
Outdated
getColumn: () => 0, | ||
getLine: () => 0, | ||
isAtStartOfLine: () => false, | ||
rawWrite: noop, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be noop
or should it raise an error when called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed it to raise an error, since rawWrite
is only used to print comments, and the display part writer should only be used when comment printing is disabled.
src/compiler/types.ts
Outdated
@@ -2651,8 +2662,13 @@ namespace ts { | |||
getTypeAtLocation(node: Node): Type; | |||
getTypeFromTypeNode(node: TypeNode): Type; | |||
signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string; | |||
/* @internal */ signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind, writer?: EmitTextWriter): string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than overloading these, why not have:
/* @internal */ writeSignature(writer: EmitTextWriter, signature: Signature, ...): void;
/* @internal */ writeType(writer: EmitTextWriter, type: Type, ...): void;
/* @internal */ writeSymbol(writer: EmitTextWriter, symbol: Symbol, ...): void;
/* @internal */ writeTypePredicate(writer: EmitTextWriter, predicate: TypePredicate, ...): void;
That is similar to how Printer
splits up internal and public members.
src/compiler/types.ts
Outdated
@@ -2841,6 +2864,9 @@ namespace ts { | |||
// eg. module m { export class c { } } import x = m.c; | |||
// When this flag is specified m.c will be used to refer to the class instead of alias symbol x | |||
UseOnlyExternalAliasing = 0x00000002, | |||
|
|||
// Build symbol name using property accesses and element accesses, instead of an entity name | |||
AllowAnyNodeKind = 0x00000004, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name AllowAnyNodeKind
doesn't seem to align with the intended use described in the comment. Perhaps a better description or a more descriptive name would be better here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the description
…hidden class is made first to reduce future polymorphism
6862e50
to
3e18178
Compare
… rename function in emitter
@rbuckton I've finally gotten around to syncing this and implementing your comments. Is there anything else you'd like to see? |
@weswigham can you please refresh, sync with @rbuckton and merge |
While working on the new declaration emitter, I recognized that there were certain parts of the current declaration emitter which relied on the symbol display builder. For the most part, we had heavily duplicated logic between the symbol display builder and the node builder. The PR fully removes the symbol display builder; replacing it with the node builder and a printer, after upgrading our emitter to do some of its writes in the more granular fashion that the symbol display writer used to expect, and altering the node builder to be capable of the little bit of extra reporting the declaration emitter expects.
This enables me to use the node builder (as one would expect) in the new declaration emitter in a similar fashion to how it is now used in the current declaration emitter.
While I've tried to keep the baseline changes to the absolute minimum, there are a few correctness changes, and a few whitespace changes. The correctness changes are for mapped types - previously when the constraint was written, it would write the declared constraint, rather than the instantiated constraint (which was noticeable in declaration emit baselines, so this has now been fixed). The whitespace changes have to do with the emitter - parameter, type parameter, and type argument lists were marked as both
SingleLine
andIndented
- causing objects written across multiple lines to have way more indents than they should - these are no longer marked asIndented
, and so no longer randomly add indentation (even though they should never add newlines on their own). This improved some of our js baselines to actually match the input, which is a good thing (in addition to making the declaration output more closely match old output). Other minor changes are that mapped types are written on a single line when synthesized, rather than on multiple lines, and that the declaration emitter more aggressively uses thetypeof
operator where possible (as before a small bug in the symbol display builder was preventing it from finding some local aliases).As the
SymbolDisplayBuilder
(andSymbolWriter
, which has been subsumed intoEmitTextWriter
) was part of our public API, this is a breaking change - however newXToString
APIs exist which take a writer and can be used to polyfill the functionality.