Skip to content

Commit

Permalink
feat(typescript): emit marked sources for functions, classes and enums (
Browse files Browse the repository at this point in the history
#5578)

Co-authored-by: Shahms King <shahms@google.com>
  • Loading branch information
nbeloglazov and shahms committed Apr 17, 2023
1 parent 2da2dcf commit 02c7c21
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 8 deletions.
71 changes: 63 additions & 8 deletions kythe/typescript/indexer.ts
Expand Up @@ -1108,6 +1108,7 @@ class Visitor {
this.emitNode(kType, NodeKind.INTERFACE);
this.emitEdge(this.newAnchor(decl.name), EdgeKind.DEFINES_BINDING, kType);
this.visitJSDoc(decl, kType);
this.emitMarkedSourceForClasslikeDeclaration(decl, kType);
}

if (decl.typeParameters)
Expand Down Expand Up @@ -1780,7 +1781,7 @@ class Visitor {
ts.isPropertyDeclaration(decl) || ts.isBindingElement(decl) ||
ts.isShorthandPropertyAssignment(decl) ||
ts.isPropertySignature(decl) || ts.isJsxAttribute(decl)) {
this.emitDeclarationCode(decl, vname);
this.emitMarkedSourceForVariable(decl, vname);
} else {
todo(this.sourceRoot, decl, 'Emit variable delaration code');
}
Expand Down Expand Up @@ -1817,10 +1818,10 @@ class Visitor {
* ((property)|(local var)|const|let) <name>: <type>( = <initializer>)?
* where `(local var)` is the declaration of a variable in a catch clause.
*/
emitDeclarationCode(
emitMarkedSourceForVariable(
decl: ts.VariableDeclaration|ts.PropertyAssignment|
ts.PropertyDeclaration|ts.BindingElement|ts.ShorthandPropertyAssignment|
ts.PropertySignature|ts.JsxAttribute,
ts.PropertySignature|ts.JsxAttribute|ts.ParameterDeclaration|ts.EnumMember,
declVName: VName) {
const codeParts: JSONMarkedSource[] = [];
const initializerList = decl.parent;
Expand Down Expand Up @@ -1854,17 +1855,24 @@ class Visitor {
'(local var)' :
initializerList.flags & ts.NodeFlags.Const ? 'const' :
'let';
} else if (ts.isParameter(varDecl)) {
declKw = '(parameter)';
} else if (ts.isEnumMember(varDecl)) {
declKw = '(enum member)';
} else {
declKw = '(property)';
}
const ty = this.typeChecker.getTypeAtLocation(decl);
const tyStr = this.typeChecker.typeToString(ty, decl);
codeParts.push({kind: MarkedSourceKind.CONTEXT, pre_text: fmtMarkedSource(declKw)});
codeParts.push({kind: MarkedSourceKind.BOX, pre_text: ' '});
codeParts.push(
{kind: MarkedSourceKind.IDENTIFIER, pre_text: fmtMarkedSource(decl.name.getText())});
codeParts.push(
{kind: MarkedSourceKind.TYPE, pre_text: ': ', post_text: fmtMarkedSource(tyStr)});
if (!ts.isEnumMember(varDecl)) {
const ty = this.typeChecker.getTypeAtLocation(decl);
const tyStr = this.typeChecker.typeToString(ty, decl);
codeParts.push(
{kind: MarkedSourceKind.TYPE, pre_text: ': ', post_text: fmtMarkedSource(tyStr)});

}
if ('initializer' in varDecl && varDecl.initializer) {
let init: ts.Node = varDecl.initializer;

Expand All @@ -1880,7 +1888,49 @@ class Visitor {
{kind: MarkedSourceKind.INITIALIZER, pre_text: fmtMarkedSource(init.getText())});
}

const markedSource = ({kind: MarkedSourceKind.BOX, child: codeParts});
const markedSource = {kind: MarkedSourceKind.BOX, child: codeParts};
this.emitFact(declVName, FactName.CODE_JSON, JSON.stringify(markedSource));
}

/**
* Emits a code fact for a class specifying how the declaration should be presented to users.
*/
emitMarkedSourceForClasslikeDeclaration(
decl: ts.ClassLikeDeclaration|ts.InterfaceDeclaration|ts.EnumDeclaration, declVName: VName) {
const markedSource: JSONMarkedSource =
{kind: MarkedSourceKind.IDENTIFIER, pre_text: decl.name?.getText() ?? 'anonymous'};
this.emitFact(declVName, FactName.CODE_JSON, JSON.stringify(markedSource));
}

/**
* Emits a code fact for a function specifying how the declaration should be presented to users.
*/
emitMarkedSourceForFunction(decl: ts.FunctionLikeDeclaration, declVName: VName) {
const codeParts: JSONMarkedSource[] = [];
const context = ts.isMethodDeclaration(decl) ? '(method)' : 'function';
codeParts.push({kind: MarkedSourceKind.CONTEXT, pre_text: context});
codeParts.push({kind: MarkedSourceKind.BOX, pre_text: ' '});
codeParts.push({
kind: MarkedSourceKind.IDENTIFIER,
pre_text: fmtMarkedSource(decl.name?.getText() ?? 'anonymous'),
});
codeParts.push({
kind: MarkedSourceKind.PARAMETER_LOOKUP_BY_PARAM,
pre_text: '(',
post_child_text: ', ',
post_text: ')'
});
const signature = this.typeChecker.getTypeAtLocation(decl).getCallSignatures()[0];
if (signature) {
const returnType = signature.getReturnType();
const returnTypeStr = this.typeChecker.typeToString(returnType, decl);
codeParts.push({
kind: MarkedSourceKind.TYPE,
pre_text: ': ',
post_text: fmtMarkedSource(returnTypeStr),
});
}
const markedSource = {kind: MarkedSourceKind.BOX, child: codeParts};
this.emitFact(declVName, FactName.CODE_JSON, JSON.stringify(markedSource));
}

Expand Down Expand Up @@ -2147,6 +2197,7 @@ class Visitor {
} else {
this.emitFact(vname, FactName.COMPLETE, 'incomplete');
}
this.emitMarkedSourceForFunction(decl, vname);
}

/**
Expand Down Expand Up @@ -2199,6 +2250,7 @@ class Visitor {

this.emitEdge(
this.newAnchor(param.name), EdgeKind.DEFINES_BINDING, kParam);
this.emitMarkedSourceForVariable(param, kParam);
break;
case ts.SyntaxKind.ObjectBindingPattern:
case ts.SyntaxKind.ArrayBindingPattern:
Expand Down Expand Up @@ -2324,6 +2376,7 @@ class Visitor {
}

this.visitJSDoc(decl, kClass);
this.emitMarkedSourceForClasslikeDeclaration(decl, kClass);
}
if (decl.typeParameters)
this.visitTypeParameters(kClass, decl.typeParameters);
Expand All @@ -2349,6 +2402,7 @@ class Visitor {
for (const member of decl.members) {
this.visit(member);
}
this.emitMarkedSourceForClasslikeDeclaration(decl, kValue);
}

visitEnumMember(decl: ts.EnumMember) {
Expand All @@ -2358,6 +2412,7 @@ class Visitor {
if (!kMember) return;
this.emitNode(kMember, NodeKind.CONSTANT);
this.emitEdge(this.newAnchor(decl.name), EdgeKind.DEFINES_BINDING, kMember);
this.emitMarkedSourceForVariable(decl, kMember);
}

visitExpressionMember(node: ts.Node) {
Expand Down
77 changes: 77 additions & 0 deletions kythe/typescript/testdata/marked_source/class.ts
@@ -0,0 +1,77 @@

//- @MyClass defines/binding MyClass
//-
//- MyClass code MyClassCode
//- MyClassCode.kind "IDENTIFIER"
//- MyClassCode.pre_text "MyClass"
class MyClass {
constructor(arg: string) {}

//- @myMethod defines/binding MyMethod
//-
//- MyMethod code MyMethodCode
//- MyMethodCode.kind "BOX"
//-
//- MyMethodCode child.0 MyMethodContext
//- MyMethodContext.kind "CONTEXT"
//- MyMethodContext.pre_text "(method)"
//-
//- MyMethodCode child.1 MyMethodSpace
//- MyMethodSpace.pre_text " "
//-
//- MyMethodCode child.2 MyMethodCodeId
//- MyMethodCodeId.kind "IDENTIFIER"
//- MyMethodCodeId.pre_text "myMethod"
//-
//- MyMethodCode child.3 MyMethodParams
//- MyMethodParams.kind "PARAMETER_LOOKUP_BY_PARAM"
//- MyMethodParams.pre_text "("
//- MyMethodParams.post_text ")"
//- MyMethodParams.post_child_text ", "
//-
//- MyMethodCode child.4 MyMethodReturnType
//- MyMethodReturnType.kind "TYPE"
//- MyMethodReturnType.pre_text ": "
//- MyMethodReturnType.post_text "MyClass"
//-
//- @arg defines/binding Arg
//- Arg code ArgCode
//- ArgCode.kind "BOX"
//-
//- ArgCode child.0 ArgCodeContext
//- ArgCodeContext.kind "CONTEXT"
//- ArgCodeContext.pre_text "(parameter)"
//-
//- ArgCode child.1 ArgCodeSpace
//- ArgCodeSpace.pre_text " "
//-
//- ArgCode child.2 ArgCodeId
//- ArgCodeId.kind "IDENTIFIER"
//- ArgCodeId.pre_text "arg"
//-
//- ArgCode child.3 ArgCodeType
//- ArgCodeType.kind "TYPE"
//- ArgCodeType.pre_text ": "
//- ArgCodeType.post_text "number"
myMethod(arg: number): MyClass {
return this;
}

// Test that return type is inferred.
//- @returnNumber defines/binding ReturnNumber
//- ReturnNumber code ReturnNumberCode
//- ReturnNumberCode child.4 ReturnNumberType
//- ReturnNumberType.kind "TYPE"
//- ReturnNumberType.pre_text ": "
//- ReturnNumberType.post_text "number"
returnNumber() {
return 42;
}
}

//- @MyInterface defines/binding MyInterface
//-
//- MyInterface code MyInterfaceCode
//- MyInterfaceCode.kind "IDENTIFIER"
//- MyInterfaceCode.pre_text "MyInterface"
interface MyInterface {}
33 changes: 33 additions & 0 deletions kythe/typescript/testdata/marked_source/enum.ts
@@ -0,0 +1,33 @@

//- @MyEnum defines/binding MyEnum
//-
//- MyEnum code MyEnumCode
//- MyEnumCode.kind "IDENTIFIER"
//- MyEnumCode.pre_text "MyEnum"
enum MyEnum {

//- @MY_VALUE defines/binding MyValue
//-
//- MyValue code MyValueCode
//- MyValueCode.kind "BOX"
//-
//- MyValueCode child.0 MyValueContext
//- MyValueContext.kind "CONTEXT"
//- MyValueContext.pre_text "(enum member)"
//-
//- MyValueCode child.1 MyValueSpace
//- MyValueSpace.pre_text " "
//-
//- MyValueCode child.2 MyValueId
//- MyValueId.kind "IDENTIFIER"
//- MyValueId.pre_text "MY_VALUE"
//-
//- MyValueCode child.3 MyValueEqual
//- MyValueEqual.kind "BOX"
//- MyValueEqual.pre_text " = "
//-
//- MyValueCode child.4 MyValueInitializer
//- MyValueInitializer.kind "INITIALIZER"
//- MyValueInitializer.pre_text "123"
MY_VALUE = 123,
}
56 changes: 56 additions & 0 deletions kythe/typescript/testdata/marked_source/function.ts
@@ -0,0 +1,56 @@

//- @myFunction defines/binding MyFunction
//- MyFunction code MyFunctionCode
//-
//- MyFunctionCode child.0 MyFunctionContext
//- MyFunctionContext.kind "CONTEXT"
//- MyFunctionContext.pre_text "function"
//-
//- MyFunctionCode child.1 MyFunctionSpace
//- MyFunctionSpace.pre_text " "
//-
//- MyFunctionCode child.2 MyFunctionName
//- MyFunctionName.kind "IDENTIFIER"
//- MyFunctionName.pre_text "myFunction"
//-
//- MyFunctionCode child.3 MyFunctionParams
//- MyFunctionParams.kind "PARAMETER_LOOKUP_BY_PARAM"
//- MyFunctionParams.pre_text "("
//- MyFunctionParams.post_text ")"
//- MyFunctionParams.post_child_text ", "
//-
//- MyFunctionCode child.4 MyFunctionReturnType
//- MyFunctionReturnType.kind "TYPE"
//- MyFunctionReturnType.pre_text ": "
//- MyFunctionReturnType.post_text "number"
//-
//- @arg defines/binding Arg
//- Arg code ArgCode
//- ArgCode.kind "BOX"
//-
//- ArgCode child.0 ArgCodeContext
//- ArgCodeContext.kind "CONTEXT"
//- ArgCodeContext.pre_text "(parameter)"
//-
//- ArgCode child.1 ArgCodeSpace
//- ArgCodeSpace.pre_text " "
//-
//- ArgCode child.2 ArgCodeId
//- ArgCodeId.kind "IDENTIFIER"
//- ArgCodeId.pre_text "arg"
//-
//- ArgCode child.3 ArgCodeType
//- ArgCodeType.kind "TYPE"
//- ArgCodeType.pre_text ": "
//- ArgCodeType.post_text "string"
//-
//- ArgCode child.4 ArgCodeEqual
//- ArgCodeEqual.kind "BOX"
//- ArgCodeEqual.pre_text " = "
//-
//- ArgCode child.5 ArgCodeDefaultValue
//- ArgCodeDefaultValue.kind "INITIALIZER"
//- ArgCodeDefaultValue.pre_text "'0'"
function myFunction(arg: string = '0'): number {
return 0;
}

0 comments on commit 02c7c21

Please sign in to comment.