Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"changes": [
{
"packageName": "@typespec/compiler",
"comment": "Fix: Anonymous union variants were formatted with an extra leading `:`",
"type": "none"
},
{
"packageName": "@typespec/compiler",
"comment": "Formatter: Unions and Enums members are now formatted following the same rules as model properties. An extra line will be added between members if the member is annotated with a decorator, directive or doc comment.",
"type": "none"
}
],
"packageName": "@typespec/compiler"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/protobuf",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/protobuf"
}
4 changes: 4 additions & 0 deletions packages/compiler/lib/decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -367,11 +367,13 @@ enum DateTimeKnownEncoding {
* Encode to string.
*/
rfc3339: "rfc3339",

/**
* RFC 7231 standard. https://www.ietf.org/rfc/rfc7231.txt
* Encode to string.
*/
rfc7231: "rfc7231",

/**
* Encode to integer
*/
Expand All @@ -386,6 +388,7 @@ enum DurationKnownEncoding {
* ISO8601 duration
*/
ISO8601: "ISO8601",

/**
* Encode to integer or float
*/
Expand All @@ -400,6 +403,7 @@ enum BytesKnownEncoding {
* Encode to Base64
*/
base64: "base64",

/**
* Encode to Base64 Url
*/
Expand Down
87 changes: 36 additions & 51 deletions packages/compiler/src/formatter/print/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -688,18 +688,8 @@ function printEnumBlock(
return "{}";
}

return group([
"{",
indent([
hardline,
join(
hardline,
path.map((x) => [print(x as any), ","], "members")
),
]),
hardline,
"}",
]);
const body = joinMembersInBlock(path, "members", options, print, ",", hardline);
return group(["{", indent(body), hardline, "}"]);
}

export function printEnumMember(
Expand All @@ -710,12 +700,10 @@ export function printEnumMember(
const node = path.getValue();
const id = path.call(print, "id");
const value = node.value ? [": ", path.call(print, "value")] : "";
const { decorators, multiline } = printDecorators(path, options, print, {
const { decorators } = printDecorators(path, options, print, {
tryInline: DecoratorsTryInline.enumMember,
});
const propertyIndex = path.stack[path.stack.length - 2];
const isNotFirst = typeof propertyIndex === "number" && propertyIndex > 0;
return [multiline && isNotFirst ? hardline : "", decorators, id, value];
return [decorators, id, value];
}

function printEnumSpreadMember(
Expand Down Expand Up @@ -747,31 +735,20 @@ export function printUnionVariantsBlock(
return "{}";
}

return group([
"{",
indent([
hardline,
join(
hardline,
path.map((x) => [print(x as any), ","], "options")
),
]),
hardline,
"}",
]);
const body = joinMembersInBlock(path, "options", options, print, ",", hardline);
return group(["{", indent(body), hardline, "}"]);
}

export function printUnionVariant(
path: AstPath<UnionVariantNode>,
options: TypeSpecPrettierOptions,
print: PrettierChildPrint
) {
const id = path.call(print, "id");
const value = [": ", path.call(print, "value")];
const id = path.node.id === undefined ? "" : [path.call(print, "id"), ": "];
const { decorators } = printDecorators(path, options, print, {
tryInline: DecoratorsTryInline.unionVariant,
});
return [decorators, id, value];
return [decorators, id, path.call(print, "value")];
}

export function printInterfaceStatement(
Expand Down Expand Up @@ -965,7 +942,9 @@ export function printModelExpression(
const properties =
node.properties.length === 0
? ""
: indent(joinPropertiesInBlock(path as any, options, print, ifBreak(",", ", "), softline));
: indent(
joinMembersInBlock(path, "properties", options, print, ifBreak(",", ", "), softline)
);
return group([properties, softline]);
}
}
Expand Down Expand Up @@ -1019,37 +998,38 @@ function printModelPropertiesBlock(
const lineDoc = tryInline ? softline : hardline;
const seperator = isModelAValue(path) ? "," : ";";

const body = [joinPropertiesInBlock(path as any, options, print, seperator, lineDoc)];
const body = [joinMembersInBlock(path, "properties", options, print, seperator, lineDoc)];
if (nodeHasComments) {
body.push(printDanglingComments(path, options, { sameIndent: true }));
}
return group(["{", indent(body), lineDoc, "}"]);
}

function joinPropertiesInBlock(
path: AstPath<
Node & {
properties: readonly (
| ModelPropertyNode
| ModelSpreadPropertyNode
| ProjectionModelPropertyNode
| ProjectionModelSpreadPropertyNode
)[];
}
>,
/**
* Join members nodes that are in a block by adding extra new lines when needed.(e.g. when there are decorators or doc comments )
* @param path Prettier AST Path.
* @param options Prettier options
* @param print Prettier print callback
* @param separator Separator
* @param regularLine What line to use when we should split lines
* @returns
*/
function joinMembersInBlock<T extends Node>(
path: AstPath<T>,
member: keyof T,
options: TypeSpecPrettierOptions,
print: PrettierChildPrint,
separator: Doc,
regularLine: Doc
regularLine: Doc = hardline
): Doc {
const doc: Doc[] = [regularLine];
const propertyContainerNode = path.getValue();

let newLineBeforeNextProp = false;
path.each((item, propertyIndex) => {
const isFirst = propertyIndex === 0;
const isLast = propertyIndex === propertyContainerNode.properties.length - 1;
const shouldWrapInNewLines = shouldWrapPropertyInNewLines(item as any, options);
const isLast = propertyIndex === (propertyContainerNode[member] as any).length - 1;
const shouldWrapInNewLines = shouldWrapMemberInNewLines(item as any, options);

if ((newLineBeforeNextProp || shouldWrapInNewLines) && !isFirst) {
doc.push(hardline);
Expand All @@ -1065,7 +1045,7 @@ function joinPropertiesInBlock(
newLineBeforeNextProp = true;
}
}
}, "properties");
}, member as any);
return doc;
}

Expand All @@ -1075,18 +1055,23 @@ function joinPropertiesInBlock(
* - has decorators on lines above
* - has leading comments
*/
function shouldWrapPropertyInNewLines(
function shouldWrapMemberInNewLines(
path: AstPath<
| ModelPropertyNode
| ModelSpreadPropertyNode
| EnumMemberNode
| EnumSpreadMemberNode
| UnionVariantNode
| ProjectionModelPropertyNode
| ProjectionModelSpreadPropertyNode
>,
options: any
): boolean {
const node = path.getValue();
return (
((node.kind === SyntaxKind.ModelProperty || node.kind === SyntaxKind.ProjectionModelProperty) &&
(node.kind !== SyntaxKind.ModelSpreadProperty &&
node.kind !== SyntaxKind.ProjectionModelSpreadProperty &&
node.kind !== SyntaxKind.EnumSpreadMember &&
shouldDecoratorBreakLine(path as any, options, {
tryInline: DecoratorsTryInline.modelProperty,
})) ||
Expand Down Expand Up @@ -1309,7 +1294,7 @@ function getLastStatement(statements: Statement[]): Statement | undefined {

export function printUnion(
path: AstPath<UnionExpressionNode>,
options: object,
options: TypeSpecPrettierOptions,
print: PrettierChildPrint
) {
const node = path.getValue();
Expand Down
67 changes: 67 additions & 0 deletions packages/compiler/test/formatter/formatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,73 @@ enum Foo {
});
});

describe("union", () => {
it("format simple union", async () => {
await assertFormat({
code: `
union Foo { A, B}
union Bar
{ A,
B}
`,
expected: `
union Foo {
A,
B,
}
union Bar {
A,
B,
}
`,
});
});

it("format named union", async () => {
await assertFormat({
code: `
union Foo { a: A, b: B}
`,
expected: `
union Foo {
a: A,
b: B,
}

`,
});
});

it("separate members if there is decorators", async () => {
await assertFormat({
code: `
union Foo {
@doc("foo")
a: A, @doc("bar")
b : B,



@doc("third")
c : C}

`,
expected: `
union Foo {
@doc("foo")
a: A,

@doc("bar")
b: B,

@doc("third")
c: C,
}
`,
});
});
});

describe("namespaces", () => {
it("format global namespace", async () => {
await assertFormat({
Expand Down
3 changes: 3 additions & 0 deletions packages/protobuf/lib/proto.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -265,16 +265,19 @@ enum StreamMode {
* other until the connections are closed.
*/
Duplex,

/**
* The input of the operation is streaming. The client will send a stream of events; and, once the stream is closed,
* the service will respond with a message.
*/
In,

/**
* The output of the operation is streaming. The client will send a message to the service, and the service will send
* a stream of events back to the client.
*/
Out,

/**
* Neither the input nor the output are streaming. This is the default mode of an operation without the `@stream`
* decorator.
Expand Down