Skip to content

Commit fce7979

Browse files
committed
Closes #7930
1 parent 6b970e4 commit fce7979

2 files changed

Lines changed: 92 additions & 26 deletions

File tree

tools/apiview/emitters/typespec-apiview/src/apiview.ts

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
EnumMemberNode,
99
EnumSpreadMemberNode,
1010
EnumStatementNode,
11+
Expression,
1112
getNamespaceFullName,
1213
getSourceLocation,
1314
IdentifierNode,
@@ -383,6 +384,7 @@ export class ApiView {
383384
this.namespaceStack.push(obj.id.sv);
384385
this.keyword("alias", false, true);
385386
this.typeDeclaration(obj.id.sv, this.namespaceStack.value(), true);
387+
this.tokenizeTemplateParameters(obj.templateParameters);
386388
this.punctuation("=", true, true);
387389
this.tokenize(obj.value);
388390
this.namespaceStack.pop();
@@ -635,26 +637,24 @@ export class ApiView {
635637
break;
636638
case SyntaxKind.StringTemplateExpression:
637639
obj = node as StringTemplateExpressionNode;
638-
const multiLine = (obj.head.value.includes("\n") || obj.spans.some(x => x.literal.value.includes("\n")))
639-
640-
if (multiLine) {
641-
this.punctuation(`"""`);
642-
this.newline();
643-
this.indent();
644-
} else {
645-
this.punctuation(`"`);
646-
}
647-
this.tokenize(obj.head);
648-
for (const span of obj.spans) {
649-
this.tokenize(span);
640+
const stringValue = this.buildTemplateString(obj);
641+
const multiLine = stringValue.includes("\n");
642+
// single line case
643+
if (!multiLine) {
644+
this.stringLiteral(stringValue);
645+
break;
650646
}
651-
if (multiLine) {
647+
// otherwise multiline case
648+
const lines = stringValue.split("\n");
649+
this.punctuation(`"""`);
650+
this.newline();
651+
this.indent();
652+
for (const line of lines) {
653+
this.literal(line);
652654
this.newline();
653-
this.deindent();
654-
this.punctuation(`"""`);
655-
} else {
656-
this.punctuation(`"`);
657655
}
656+
this.deindent();
657+
this.punctuation(`"""`);
658658
break;
659659
case SyntaxKind.StringTemplateSpan:
660660
obj = node as StringTemplateSpanNode;
@@ -670,11 +670,50 @@ export class ApiView {
670670
this.literal(obj.value);
671671
break;
672672
default:
673-
// All Projection* cases should fall in here...
673+
// All Projection* cases should fail here...
674674
throw new Error(`Case "${SyntaxKind[node.kind].toString()}" not implemented`);
675675
}
676676
}
677677

678+
private buildExpressionString(node: Expression) {
679+
switch (node.kind) {
680+
case SyntaxKind.StringLiteral:
681+
return `"${(node as StringLiteralNode).value}"`;
682+
case SyntaxKind.NumericLiteral:
683+
return (node as NumericLiteralNode).value.toString();
684+
case SyntaxKind.BooleanLiteral:
685+
return (node as BooleanLiteralNode).value.toString();
686+
case SyntaxKind.StringTemplateExpression:
687+
return this.buildTemplateString(node as StringTemplateExpressionNode);
688+
case SyntaxKind.VoidKeyword:
689+
return "void";
690+
case SyntaxKind.NeverKeyword:
691+
return "never";
692+
case SyntaxKind.TypeReference:
693+
const obj = node as TypeReferenceNode;
694+
switch (obj.target.kind) {
695+
case SyntaxKind.Identifier:
696+
return (obj.target as IdentifierNode).sv;
697+
case SyntaxKind.MemberExpression:
698+
return this.getFullyQualifiedIdentifier(obj.target as MemberExpressionNode);
699+
}
700+
break;
701+
default:
702+
throw new Error(`Unsupported expression kind: ${SyntaxKind[node.kind]}`);
703+
//unsupported ArrayExpressionNode | MemberExpressionNode | ModelExpressionNode | TupleExpressionNode | UnionExpressionNode | IntersectionExpressionNode | TypeReferenceNode | ValueOfExpressionNode | AnyKeywordNode;
704+
}
705+
}
706+
707+
/** Constructs a single string with template markers. */
708+
private buildTemplateString(node: StringTemplateExpressionNode): string {
709+
let result = node.head.value;
710+
for (const span of node.spans) {
711+
result += "${" + this.buildExpressionString(span.expression) + "}";
712+
result += span.literal.value;
713+
}
714+
return result;
715+
}
716+
678717
private tokenizeModelStatement(node: ModelStatementNode) {
679718
this.namespaceStack.push(node.id.sv);
680719
this.tokenizeDecorators(node.decorators, false);
@@ -927,6 +966,7 @@ export class ApiView {
927966
}
928967
for (const node of model.aliases.values()) {
929968
this.tokenize(node);
969+
this.punctuation(";");
930970
this.blankLines(1);
931971
}
932972
this.endGroup();

tools/apiview/emitters/typespec-apiview/test/apiview.test.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,15 +243,41 @@ describe("apiview: tests", () => {
243243
species: string;
244244
}
245245
246-
alias Creature = Animal
246+
alias Creature = Animal;
247247
}
248248
`;
249249
const apiview = await apiViewFor(input, {});
250250
const actual = apiViewText(apiview);
251251
compare(expect, actual, 9);
252252
validateDefinitionIds(apiview);
253253
});
254-
});
254+
255+
it("templated alias", async () => {
256+
const input = `
257+
@TypeSpec.service( { title: "Test", version: "1" } )
258+
namespace Azure.Test {
259+
model Animal {
260+
species: string;
261+
}
262+
263+
alias Template<T extends valueof string> = "Foo \${T} bar";
264+
}
265+
`;
266+
const expect = `
267+
namespace Azure.Test {
268+
model Animal {
269+
species: string;
270+
}
271+
272+
alias Template<T extends valueof string> = "Foo \${T} bar";
273+
}
274+
`;
275+
const apiview = await apiViewFor(input, {});
276+
const actual = apiViewText(apiview);
277+
compare(expect, actual, 9);
278+
validateDefinitionIds(apiview);
279+
});
280+
});
255281

256282
describe("augment decorators", () => {
257283
it("simple augment", async () => {
@@ -704,8 +730,8 @@ describe("apiview: tests", () => {
704730
simple: "Simple \${123} end";
705731
multiline: """
706732
Multi
707-
\${123}
708-
\${true}
733+
\${123}
734+
\${true}
709735
line
710736
""";
711737
ref: "Ref this alias \${myconst} end";
@@ -719,10 +745,10 @@ describe("apiview: tests", () => {
719745
model Person {
720746
simple: "Simple \${123} end";
721747
multiline: """
722-
Multi
723-
\${123}
724-
\${true}
725-
line
748+
Multi
749+
\${123}
750+
\${true}
751+
line
726752
""";
727753
ref: "Ref this alias \${myconst} end";
728754
template: Template<"custom">;

0 commit comments

Comments
 (0)