Add support for vscode-js-debug's customDescriptionGenerators (#40308)

This commit is contained in:
Ron Buckton 2020-09-02 20:05:50 -07:00 committed by GitHub
parent 38cedc5b5f
commit d89635381a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 188 additions and 9 deletions

View File

@ -47,14 +47,24 @@
"console": "integratedTerminal",
"outFiles": [
"${workspaceRoot}/built/local/run.js"
]
],
// NOTE: To use this, you must switch the "type" above to "pwa-node". You may also need to follow the instructions
// here: https://github.com/microsoft/vscode-js-debug#nightly-extension to use the js-debug nightly until
// this feature is shipping in insiders or to a release:
// "customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue"
},
{
// See: https://github.com/microsoft/TypeScript/wiki/Debugging-Language-Service-in-VS-Code
"type": "node",
"request": "attach",
"name": "Attach to VS Code TS Server via Port",
"processId": "${command:PickProcess}"
"processId": "${command:PickProcess}",
// NOTE: To use this, you must switch the "type" above to "pwa-node". You may also need to follow the instructions
// here: https://github.com/microsoft/vscode-js-debug#nightly-extension to use the js-debug nightly until
// this feature is shipping in insiders or to a release:
// "customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue"
}
]
}

View File

@ -371,6 +371,10 @@ namespace ts {
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
}
export function formatFlowFlags(flags: FlowFlags | undefined): string {
return formatEnum(flags, (<any>ts).FlowFlags, /*isFlags*/ true);
}
let isDebugInfoEnabled = false;
interface ExtendedDebugModule {
@ -396,13 +400,87 @@ namespace ts {
return extendedDebug().formatControlFlowGraph(flowNode);
}
export function attachFlowNodeDebugInfo(flowNode: FlowNode) {
let flowNodeProto: FlowNodeBase | undefined;
function attachFlowNodeDebugInfoWorker(flowNode: FlowNodeBase) {
if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line no-in-operator
Object.defineProperties(flowNode, {
// for use with vscode-js-debug's new customDescriptionGenerator in launch.json
__tsDebuggerDisplay: {
value(this: FlowNodeBase) {
const flowHeader =
this.flags & FlowFlags.Start ? "FlowStart" :
this.flags & FlowFlags.BranchLabel ? "FlowBranchLabel" :
this.flags & FlowFlags.LoopLabel ? "FlowLoopLabel" :
this.flags & FlowFlags.Assignment ? "FlowAssignment" :
this.flags & FlowFlags.TrueCondition ? "FlowTrueCondition" :
this.flags & FlowFlags.FalseCondition ? "FlowFalseCondition" :
this.flags & FlowFlags.SwitchClause ? "FlowSwitchClause" :
this.flags & FlowFlags.ArrayMutation ? "FlowArrayMutation" :
this.flags & FlowFlags.Call ? "FlowCall" :
this.flags & FlowFlags.ReduceLabel ? "FlowReduceLabel" :
this.flags & FlowFlags.Unreachable ? "FlowUnreachable" :
"UnknownFlow";
const remainingFlags = this.flags & ~(FlowFlags.Referenced - 1);
return `${flowHeader}${remainingFlags ? ` (${formatFlowFlags(remainingFlags)})`: ""}`;
}
},
__debugFlowFlags: { get(this: FlowNodeBase) { return formatEnum(this.flags, (ts as any).FlowFlags, /*isFlags*/ true); } },
__debugToString: { value(this: FlowNodeBase) { return formatControlFlowGraph(this); } }
});
}
}
export function attachFlowNodeDebugInfo(flowNode: FlowNodeBase) {
if (isDebugInfoEnabled) {
if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line no-in-operator
Object.defineProperties(flowNode, {
__debugFlowFlags: { get(this: FlowNode) { return formatEnum(this.flags, (ts as any).FlowFlags, /*isFlags*/ true); } },
__debugToString: { value(this: FlowNode) { return formatControlFlowGraph(this); } }
});
if (typeof Object.setPrototypeOf === "function") {
// if we're in es2015, attach the method to a shared prototype for `FlowNode`
// so the method doesn't show up in the watch window.
if (!flowNodeProto) {
flowNodeProto = Object.create(Object.prototype) as FlowNodeBase;
attachFlowNodeDebugInfoWorker(flowNodeProto);
}
Object.setPrototypeOf(flowNode, flowNodeProto);
}
else {
// not running in an es2015 environment, attach the method directly.
attachFlowNodeDebugInfoWorker(flowNode);
}
}
}
let nodeArrayProto: NodeArray<Node> | undefined;
function attachNodeArrayDebugInfoWorker(array: NodeArray<Node>) {
if (!("__tsDebuggerDisplay" in array)) { // eslint-disable-line no-in-operator
Object.defineProperties(array, {
__tsDebuggerDisplay: {
value(this: NodeArray<Node>, defaultValue: string) {
// An `Array` with extra properties is rendered as `[A, B, prop1: 1, prop2: 2]`. Most of
// these aren't immediately useful so we trim off the `prop1: ..., prop2: ...` part from the
// formatted string.
defaultValue = String(defaultValue).replace(/(?:,[\s\w\d_]+:[^,]+)+\]$/, "]");
return `NodeArray ${defaultValue}`;
}
}
});
}
}
export function attachNodeArrayDebugInfo(array: NodeArray<Node>) {
if (isDebugInfoEnabled) {
if (typeof Object.setPrototypeOf === "function") {
// if we're in es2015, attach the method to a shared prototype for `NodeArray`
// so the method doesn't show up in the watch window.
if (!nodeArrayProto) {
nodeArrayProto = Object.create(Array.prototype) as NodeArray<Node>;
attachNodeArrayDebugInfoWorker(nodeArrayProto);
}
Object.setPrototypeOf(array, nodeArrayProto);
}
else {
// not running in an es2015 environment, attach the method directly.
attachNodeArrayDebugInfoWorker(array);
}
}
}
@ -434,10 +512,51 @@ namespace ts {
// Add additional properties in debug mode to assist with debugging.
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
// for use with vscode-js-debug's new customDescriptionGenerator in launch.json
__tsDebuggerDisplay: {
value(this: Symbol) {
const symbolHeader =
this.flags & SymbolFlags.Transient ? "TransientSymbol" :
"Symbol";
const remainingSymbolFlags = this.flags & ~SymbolFlags.Transient;
return `${symbolHeader} '${symbolName(this)}'${remainingSymbolFlags ? ` (${formatSymbolFlags(remainingSymbolFlags)})` : ""}`;
}
},
__debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
});
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
// for use with vscode-js-debug's new customDescriptionGenerator in launch.json
__tsDebuggerDisplay: {
value(this: Type) {
const typeHeader =
this.flags & TypeFlags.Nullable ? "NullableType" :
this.flags & TypeFlags.StringOrNumberLiteral ? `LiteralType ${JSON.stringify((this as LiteralType).value)}` :
this.flags & TypeFlags.BigIntLiteral ? `LiteralType ${(this as BigIntLiteralType).value.negative ? "-" : ""}${(this as BigIntLiteralType).value.base10Value}n` :
this.flags & TypeFlags.UniqueESSymbol ? "UniqueESSymbolType" :
this.flags & TypeFlags.Enum ? "EnumType" :
this.flags & TypeFlags.Intrinsic ? `IntrinsicType ${(this as IntrinsicType).intrinsicName}` :
this.flags & TypeFlags.Union ? "UnionType" :
this.flags & TypeFlags.Intersection ? "IntersectionType" :
this.flags & TypeFlags.Index ? "IndexType" :
this.flags & TypeFlags.IndexedAccess ? "IndexedAccessType" :
this.flags & TypeFlags.Conditional ? "ConditionalType" :
this.flags & TypeFlags.Substitution ? "SubstitutionType" :
this.flags & TypeFlags.TypeParameter ? "TypeParameter" :
this.flags & TypeFlags.Object ?
(this as ObjectType).objectFlags & ObjectFlags.ClassOrInterface ? "InterfaceType" :
(this as ObjectType).objectFlags & ObjectFlags.Reference ? "TypeReference" :
(this as ObjectType).objectFlags & ObjectFlags.Tuple ? "TupleType" :
(this as ObjectType).objectFlags & ObjectFlags.Anonymous ? "AnonymousType" :
(this as ObjectType).objectFlags & ObjectFlags.Mapped ? "MappedType" :
(this as ObjectType).objectFlags & ObjectFlags.ReverseMapped ? "ReverseMappedType" :
(this as ObjectType).objectFlags & ObjectFlags.EvolvingArray ? "EvolvingArrayType" :
"ObjectType" :
"Type";
const remainingObjectFlags = this.flags & TypeFlags.Object ? (this as ObjectType).objectFlags & ~ObjectFlags.ObjectTypeKindMask : 0;
return `${typeHeader}${this.symbol ? ` '${symbolName(this.symbol)}'` : ""}${remainingObjectFlags ? ` (${formatObjectFlags(remainingObjectFlags)})` : ""}`;
}
},
__debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
__debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
__debugTypeToString: {
@ -464,6 +583,50 @@ namespace ts {
for (const ctor of nodeConstructors) {
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
Object.defineProperties(ctor.prototype, {
// for use with vscode-js-debug's new customDescriptionGenerator in launch.json
__tsDebuggerDisplay: {
value(this: Node) {
const nodeHeader =
isGeneratedIdentifier(this) ? "GeneratedIdentifier" :
isIdentifier(this) ? `Identifier '${idText(this)}'` :
isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` :
isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` :
isNumericLiteral(this) ? `NumericLiteral ${this.text}` :
isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` :
isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" :
isParameter(this) ? "ParameterDeclaration" :
isConstructorDeclaration(this) ? "ConstructorDeclaration" :
isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" :
isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" :
isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" :
isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" :
isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" :
isTypePredicateNode(this) ? "TypePredicateNode" :
isTypeReferenceNode(this) ? "TypeReferenceNode" :
isFunctionTypeNode(this) ? "FunctionTypeNode" :
isConstructorTypeNode(this) ? "ConstructorTypeNode" :
isTypeQueryNode(this) ? "TypeQueryNode" :
isTypeLiteralNode(this) ? "TypeLiteralNode" :
isArrayTypeNode(this) ? "ArrayTypeNode" :
isTupleTypeNode(this) ? "TupleTypeNode" :
isOptionalTypeNode(this) ? "OptionalTypeNode" :
isRestTypeNode(this) ? "RestTypeNode" :
isUnionTypeNode(this) ? "UnionTypeNode" :
isIntersectionTypeNode(this) ? "IntersectionTypeNode" :
isConditionalTypeNode(this) ? "ConditionalTypeNode" :
isInferTypeNode(this) ? "InferTypeNode" :
isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" :
isThisTypeNode(this) ? "ThisTypeNode" :
isTypeOperatorNode(this) ? "TypeOperatorNode" :
isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" :
isMappedTypeNode(this) ? "MappedTypeNode" :
isLiteralTypeNode(this) ? "LiteralTypeNode" :
isNamedTupleMember(this) ? "NamedTupleMember" :
isImportTypeNode(this) ? "ImportTypeNode" :
formatSyntaxKind(this.kind);
return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`;
}
},
__debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
__debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } },
__debugModifierFlags: { get(this: Node) { return formatModifierFlags(getEffectiveModifierFlagsNoCache(this)); } },

View File

@ -509,6 +509,7 @@ namespace ts {
if (elements.transformFlags === undefined) {
aggregateChildrenFlags(elements as MutableNodeArray<T>);
}
Debug.attachNodeArrayDebugInfo(elements);
return elements;
}
@ -520,6 +521,7 @@ namespace ts {
setTextRangePosEnd(array, -1, -1);
array.hasTrailingComma = !!hasTrailingComma;
aggregateChildrenFlags(array);
Debug.attachNodeArrayDebugInfo(array);
return array;
}

View File

@ -5021,7 +5021,11 @@ namespace ts {
/* @internal */
RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral,
/* @internal */
PropagatingFlags = ContainsWideningType | ContainsObjectOrArrayLiteral | NonInferrableType
PropagatingFlags = ContainsWideningType | ContainsObjectOrArrayLiteral | NonInferrableType,
// Object flags that uniquely identify the kind of ObjectType
/* @internal */
ObjectTypeKindMask = ClassOrInterface | Reference | Tuple | Anonymous | Mapped | ReverseMapped | EvolvingArray,
}
/* @internal */