-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
| if (!flags) { | ||
| 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?
| 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
| 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
| 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
| 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
| 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
SingleLineandIndented- 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 thetypeofoperator 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 newXToStringAPIs exist which take a writer and can be used to polyfill the functionality.