Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Improve formatting validation in tests.
  • Loading branch information
tjprescott committed Nov 8, 2024
commit c45ecc83a4e558f7a30c5f4d3fbabbfd6191c01b
22 changes: 12 additions & 10 deletions tools/apiview/emitters/typespec-apiview/main.tsp
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
@service({
name: "Magical Foo Stuff",
version: "1",
})
namespace FooStuff;

@doc("The FOO!!!!")
model Foo {
name: string;
age: int16;
#suppress "deprecated"
@TypeSpec.service( { title: "Test", version: "1" } )
namespace Azure.Test {
model Foo {};
}

namespace Azure.Test.Sub {
model SubFoo {};
};

namespace Azure.TestBad {
model BadFoo {};
};
55 changes: 22 additions & 33 deletions tools/apiview/emitters/typespec-apiview/src/apiview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,13 @@ export class ApiView {
});
}

private indent() {
private indent() {
// enure no trailing space at the end of the line
const lastToken = this.currentLine.Tokens[this.currentLine.Tokens.length - 1];
if (lastToken && lastToken.HasPrefixSpace) {
lastToken.HasSuffixSpace = false;
}

if (this.currentParent) {
this.currentParent.Children.push(this.currentLine);
this.parentStack.push(this.currentParent);
Expand All @@ -171,6 +177,11 @@ export class ApiView {
if (!this.currentParent) {
throw new Error("Cannot deindent without an indent.");
}
// Ensure that the last line before the deindent has no blank lines
const lastChild = this.currentParent.Children.pop();
if (lastChild && lastChild.Tokens.length > 0) {
this.currentParent.Children.push(lastChild);
}
this.currentParent = this.parentStack.pop();
this.currentLine = {
LineId: "",
Expand All @@ -180,14 +191,6 @@ export class ApiView {
}
}

private leadingSpace() {
if (this.currentLine.Tokens.length > 0) {
this.currentLine.Tokens[this.currentLine.Tokens.length - 1].HasSuffixSpace = true;
} else {
this.token(TokenKind.Text, " ");
}
}

/** Set the exact number of desired newlines. */
private blankLines(count: number = 0) {
this.newline();
Expand Down Expand Up @@ -215,6 +218,11 @@ export class ApiView {
}

private newline(isEndContext?: boolean) {
// ensure no trailing space at the end of the line
if (this.currentLine.Tokens.length > 0) {
const lastToken = this.currentLine.Tokens[this.currentLine.Tokens.length - 1];
lastToken.HasSuffixSpace = false;
}
if (isEndContext) {
this.currentLine.IsContextEndLine = true;
}
Expand Down Expand Up @@ -334,9 +342,8 @@ export class ApiView {
obj = node as AliasStatementNode;
this.namespaceStack.push(obj.id.sv);
this.keyword("alias", {HasSuffixSpace: true});
this.typeDeclaration(obj.id.sv, this.namespaceStack.value(), true, {HasSuffixSpace: true});
this.typeDeclaration(obj.id.sv, this.namespaceStack.value(), true);
this.tokenizeTemplateParameters(obj.templateParameters);
this.leadingSpace();
this.punctuation("=", {HasSuffixSpace: true, HasPrefixSpace: true});
this.tokenize(obj.value);
this.namespaceStack.pop();
Expand Down Expand Up @@ -396,7 +403,6 @@ export class ApiView {
this.namespaceStack.push(obj.id.sv);
this.keyword("const", {HasSuffixSpace: true});
this.tokenizeIdentifier(obj.id, "declaration");
this.leadingSpace();
this.punctuation("=", {HasSuffixSpace: true, HasPrefixSpace: true});
this.tokenize(obj.value);
this.namespaceStack.pop();
Expand Down Expand Up @@ -550,7 +556,7 @@ export class ApiView {
break;
case SyntaxKind.OperationSignatureReference:
obj = node as OperationSignatureReferenceNode;
this.keyword("is", {HasSuffixSpace: true});
this.keyword("is", {HasPrefixSpace: true, HasSuffixSpace: true});
this.tokenize(obj.baseOperation);
break;
case SyntaxKind.Return:
Expand All @@ -566,12 +572,10 @@ export class ApiView {
obj = node as TemplateParameterDeclarationNode;
this.tokenize(obj.id);
if (obj.constraint) {
this.leadingSpace();
this.keyword("extends", {HasSuffixSpace: true, HasPrefixSpace: true});
this.tokenize(obj.constraint);
}
if (obj.default) {
this.leadingSpace();
this.punctuation("=", {HasSuffixSpace: true, HasPrefixSpace: true});
this.tokenize(obj.default);
}
Expand Down Expand Up @@ -633,7 +637,7 @@ export class ApiView {
this.tokenizeUnionVariant(node as UnionVariantNode);
break;
case SyntaxKind.UnknownKeyword:
this.keyword("unknown", {HasSuffixSpace: true});
this.keyword("unknown");
break;
case SyntaxKind.UsingStatement:
throw new Error(`Case "UsingStatement" not implemented`);
Expand All @@ -652,7 +656,6 @@ export class ApiView {
}
if (obj.name) {
this.text(obj.name.sv);
this.leadingSpace();
this.punctuation("=", {HasSuffixSpace: true, HasPrefixSpace: true});
}
if (isExpanded) {
Expand Down Expand Up @@ -745,17 +748,15 @@ export class ApiView {
this.keyword("model", {HasSuffixSpace: true});
this.tokenizeIdentifier(node.id, "declaration");
if (node.extends) {
this.leadingSpace();
this.keyword("extends", {HasSuffixSpace: true, HasPrefixSpace: true});
this.tokenize(node.extends);
}
if (node.is) {
this.keyword("is", {HasSuffixSpace: true});
this.keyword("is", {HasPrefixSpace: true, HasSuffixSpace: true});
this.tokenize(node.is);
}
this.tokenizeTemplateParameters(node.templateParameters);
if (node.properties.length) {
this.leadingSpace();
this.punctuation("{", {HasPrefixSpace: true});
this.indent();
for (const prop of node.properties) {
Expand All @@ -769,7 +770,6 @@ export class ApiView {
this.deindent();
this.punctuation("}");
} else {
this.leadingSpace();
this.punctuation("{}", {HasPrefixSpace: true});
}
this.namespaceStack.pop();
Expand All @@ -781,7 +781,6 @@ export class ApiView {
this.keyword("scalar", {HasSuffixSpace: true});
this.tokenizeIdentifier(node.id, "declaration");
if (node.extends) {
this.leadingSpace();
this.keyword("extends", {HasSuffixSpace: true, HasPrefixSpace: true});
this.tokenize(node.extends);
}
Expand All @@ -796,7 +795,6 @@ export class ApiView {
this.keyword("interface", {HasSuffixSpace: true});
this.tokenizeIdentifier(node.id, "declaration");
this.tokenizeTemplateParameters(node.templateParameters);
this.leadingSpace();
this.punctuation("{", {HasPrefixSpace: true});
this.indent();
for (let x = 0; x < node.operations.length; x++) {
Expand All @@ -814,7 +812,6 @@ export class ApiView {
this.tokenizeDecoratorsAndDirectives(node.decorators, node.directives, false);
this.keyword("enum", {HasSuffixSpace: true});
this.tokenizeIdentifier(node.id, "declaration");
this.leadingSpace();
this.punctuation("{", {HasPrefixSpace: true});
this.indent();
for (const member of node.members) {
Expand All @@ -835,7 +832,6 @@ export class ApiView {
this.tokenizeDecoratorsAndDirectives(node.decorators, node.directives, false);
this.keyword("union", {HasSuffixSpace: true});
this.tokenizeIdentifier(node.id, "declaration");
this.leadingSpace();
this.punctuation("{", {HasPrefixSpace: true});
this.indent();
for (let x = 0; x < node.options.length; x++) {
Expand Down Expand Up @@ -871,7 +867,6 @@ export class ApiView {
this.punctuation(node.optional ? "?:" : ":", {HasSuffixSpace: true});
this.tokenize(node.value);
if (node.default) {
this.leadingSpace();
this.punctuation("=", {HasSuffixSpace: true, HasPrefixSpace: true});
this.tokenize(node.default);
}
Expand All @@ -880,7 +875,6 @@ export class ApiView {
private tokenizeModelExpressionInline(node: ModelExpressionNode, isOperationSignature: boolean) {
if (node.properties.length) {
if (!isOperationSignature) {
this.leadingSpace();
this.punctuation("{", {HasSuffixSpace: true, HasPrefixSpace: true});
}
for (let x = 0; x < node.properties.length; x++) {
Expand Down Expand Up @@ -947,7 +941,6 @@ export class ApiView {
}
this.namespaceStack.pop();
} else if (!isOperationSignature) {
this.leadingSpace();
this.punctuation("{}", {HasPrefixSpace: true});
}
}
Expand Down Expand Up @@ -980,7 +973,6 @@ export class ApiView {
}
this.keyword("namespace", {HasSuffixSpace: true});
this.typeDeclaration(model.name, this.namespaceStack.value(), true, {HasSuffixSpace: true});
this.leadingSpace();
this.punctuation("{", {HasPrefixSpace: true});
this.indent();
for (const node of model.augmentDecorators) {
Expand Down Expand Up @@ -1032,9 +1024,6 @@ export class ApiView {
this.namespaceStack.push(generateId(node)!);
const isDoc = docDecorators.includes((node.target as IdentifierNode).sv);
this.tokenize(node);
// if (inline) {
// this.leadingSpace();
// }
this.namespaceStack.pop();
if (isDoc) {
for (const token of this.currentLine.Tokens) {
Expand Down Expand Up @@ -1086,7 +1075,7 @@ export class ApiView {
case SyntaxKind.Identifier:
switch (style) {
case "declaration":
this.typeDeclaration(node.sv, this.namespaceStack.value(), true, {HasSuffixSpace: true});
this.typeDeclaration(node.sv, this.namespaceStack.value(), true, {HasSuffixSpace: false});
break;
case "reference":
const defId = this.definitionIdFor(node.sv, this.packageName);
Expand Down
24 changes: 12 additions & 12 deletions tools/apiview/emitters/typespec-apiview/test/apiview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ import { fail } from "assert";
describe("apiview: tests", () => {
/** Validates that there are no repeat defintion IDs. */
function validateLineIds(apiview: CodeFile) {
// FIXME: Re-enable these once the syntax renders correctly.
return;
const definitionIds = new Set<string>();
for (const line of apiview.ReviewLines) {
// ensure that there are no repeated definition IDs.
if (line.LineId !== undefined) {
if (definitionIds.has(line.LineId)) {
fail(`Duplicate defintion ID ${line.LineId}.`);
}
definitionIds.add(line.LineId);
}
}
// FIXME: Re-enable these once the syntax renders correctly.
// const definitionIds = new Set<string>();
// for (const line of apiview.ReviewLines) {
// // ensure that there are no repeated definition IDs.
// if (line.LineId !== undefined) {
// if (definitionIds.has(line.LineId)) {
// fail(`Duplicate defintion ID ${line.LineId}.`);
// }
// definitionIds.add(line.LineId);
// }
// }
}

describe("models", () => {
Expand Down Expand Up @@ -170,7 +170,7 @@ describe("apiview: tests", () => {
model Foo {
name: string = "foo";
array: string[] = #["a", "b"];
obj: Record< unknown > = #{val: 1, name: "foo"};
obj: Record<unknown> = #{val: 1, name: "foo"};
}
}
`;
Expand Down
19 changes: 10 additions & 9 deletions tools/apiview/emitters/typespec-apiview/test/test-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function apiViewText(apiview: CodeFile): string[] {
return apiview.ReviewLines.map(l => reviewLineText(l, 0)).join("\n").split("\n");
}

function getIndex(lines: string[]): number {
function getBaseIndent(lines: string[]): number {
for (const line of lines) {
if (line.trim() !== "") {
return line.length - line.trimStart().length;
Expand All @@ -77,15 +77,16 @@ function getIndex(lines: string[]): number {
/** Eliminates leading indentation and blank links that can mess with comparisons */
function trimLines(lines: string[]): string[] {
const trimmed: string[] = [];
const indent = getIndex(lines);
const indent = getBaseIndent(lines);

// if first line is blank, skip it
if (lines[0].trim() === "") {
lines = lines.slice(1);
}

for (const line of lines) {
if (line.trim() === '') {
// skip blank lines
continue;
} else {
// remove any leading indentation
trimmed.push(line.substring(indent));
}
// remove any leading indentation
trimmed.push(line.substring(indent));
}
return trimmed;
}
Expand Down