Skip to content

Commit

Permalink
Add support for operation templates and operation signature reuse (#552)
Browse files Browse the repository at this point in the history
* Add support for operation templates and operation signature reuse

* Replace `OperationInstanceNode` with `OperationSignature` types

* Add Rush change file

* Skip templated operations when scanning for operation routes

* Add operation signatures sample

* Fix formatter output for templated operation references

* Create a scope for operation statements to bind template parameters

* Add another operation signature to the signatures sample

* Add an example of operation signatures used in a templated interface

* Update language grammar specification for new syntax

* Format signatures.cadl

* Convert `OperationSignature` types to real syntax nodes

* Use `expectTokenIsOneOf`

* Update `InterfaceMember` in language specification

* Use `op is Sig` syntax instead of previous `op: Sig` syntax

* Non-working updates to tmLanguage grammar

* Nick's suggested changes

* Update tmLanguage and tests

* Add a tmLanguage test for templated operation signatures

* Add Rush change file for vscode extension
  • Loading branch information
daviwil authored Jun 2, 2022
1 parent dee20cf commit 660f3c6
Show file tree
Hide file tree
Showing 20 changed files with 972 additions and 291 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cadl-lang/compiler",
"comment": "Add support for operation templates and operation signature reuse",
"type": "minor"
}
],
"packageName": "@cadl-lang/compiler"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cadl-lang/rest",
"comment": "Skip templated operations when scanning for operation routes",
"type": "patch"
}
],
"packageName": "@cadl-lang/rest"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "cadl-vscode",
"comment": "Update tmLanguage grammar for operation signature support",
"type": "minor"
}
],
"packageName": "cadl-vscode"
}
410 changes: 213 additions & 197 deletions docs/spec.html

Large diffs are not rendered by default.

43 changes: 23 additions & 20 deletions packages/cadl-vscode/src/tmlanguage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,20 +388,6 @@ const namespaceStatement: BeginEndRule = {
patterns: [token, namespaceName, namespaceBody],
};

const functionName: MatchRule = {
key: "function-name",
scope: "entity.name.function.cadl",
match: identifier,
};

const operationName: BeginEndRule = {
key: "operation-name",
scope: meta,
begin: beforeIdentifier,
end: `((?=\\()|${universalEnd})`,
patterns: [token, functionName],
};

const operationParameters: BeginEndRule = {
key: "operation-parameters",
scope: meta,
Expand All @@ -416,22 +402,39 @@ const operationParameters: BeginEndRule = {
patterns: [token, decorator, modelProperty, modelSpreadProperty, punctuationComma],
};

const operationStatement: BeginEndRule = {
key: "operation-statement",
const operationHeritage: BeginEndRule = {
key: "operation-heritage",
scope: meta,
begin: "\\b(op)\\b",
begin: "\\b(is)\\b",
beginCaptures: {
"1": { scope: "keyword.other.cadl" },
},
end: universalEnd,
patterns: [expression],
};

const operationSignature: IncludeRule = {
key: "operation-signature",
patterns: [
token,
operationName,
typeArguments,
operationHeritage,
operationParameters,
typeAnnotation, // return type
],
};

const operationStatement: BeginEndRule = {
key: "operation-statement",
scope: meta,
begin: `\\b(op)\\b\\s+(${identifier})`,
beginCaptures: {
"1": { scope: "keyword.other.cadl" },
"2": { scope: "entity.name.function.cadl" },
},
end: universalEnd,
patterns: [token, operationSignature],
};

const interfaceMember: BeginEndRule = {
key: "interface-member",
scope: meta,
Expand All @@ -441,7 +444,7 @@ const interfaceMember: BeginEndRule = {
"2": { scope: "entity.name.function.cadl" },
},
end: universalEnd,
patterns: [token, operationParameters, typeAnnotation],
patterns: [token, operationSignature],
};

const interfaceHeritage: BeginEndRule = {
Expand Down
20 changes: 20 additions & 0 deletions packages/cadl-vscode/test/interface.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,24 @@ describe("vscode: tmlanguage: interfaces", () => {
Token.punctuation.closeBrace,
]);
});

it("interface operation that copies the signature of another operation", async () => {
const tokens = await tokenize(`
interface Foo {
bar is ResourceRead<Widget>
}`);

deepStrictEqual(tokens, [
Token.keywords.interface,
Token.identifiers.type("Foo"),
Token.punctuation.openBrace,
Token.identifiers.functionName("bar"),
Token.keywords.is,
Token.identifiers.type("ResourceRead"),
Token.punctuation.typeParameters.begin,
Token.identifiers.type("Widget"),
Token.punctuation.typeParameters.end,
Token.punctuation.closeBrace,
]);
});
});
33 changes: 33 additions & 0 deletions packages/cadl-vscode/test/operation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,37 @@ describe("vscode: tmlanguage: Operations", () => {
Token.identifiers.type("string"),
]);
});

it("operation that copies the signature of another operation", async () => {
const tokens = await tokenize("op foo is ResourceRead<Widget>");
deepStrictEqual(tokens, [
Token.keywords.operation,
Token.identifiers.functionName("foo"),
Token.keywords.is,
Token.identifiers.type("ResourceRead"),
Token.punctuation.typeParameters.begin,
Token.identifiers.type("Widget"),
Token.punctuation.typeParameters.end,
]);
});

it("defining a templated operation signature", async () => {
const tokens = await tokenize(
"op ResourceRead<TResource> is ResourceReadBase<TResource, DefaultOptions>"
);
deepStrictEqual(tokens, [
Token.keywords.operation,
Token.identifiers.functionName("ResourceRead"),
Token.punctuation.typeParameters.begin,
Token.identifiers.type("TResource"),
Token.punctuation.typeParameters.end,
Token.keywords.is,
Token.identifiers.type("ResourceReadBase"),
Token.punctuation.typeParameters.begin,
Token.identifiers.type("TResource"),
Token.punctuation.comma,
Token.identifiers.type("DefaultOptions"),
Token.punctuation.typeParameters.end,
]);
});
});
2 changes: 2 additions & 0 deletions packages/compiler/core/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ export function createBinder(program: Program, options: BinderOptions = {}): Bin
function bindOperationStatement(statement: OperationStatementNode) {
if (scope.kind !== SyntaxKind.InterfaceStatement) {
declareSymbol(statement, SymbolFlags.Operation);
statement.locals = new SymbolTable();
}
}

Expand Down Expand Up @@ -451,6 +452,7 @@ function hasScope(node: Node): node is ScopeNode {
case SyntaxKind.AliasStatement:
case SyntaxKind.CadlScript:
case SyntaxKind.InterfaceStatement:
case SyntaxKind.OperationStatement:
case SyntaxKind.UnionStatement:
case SyntaxKind.Projection:
case SyntaxKind.ProjectionLambdaExpression:
Expand Down
Loading

0 comments on commit 660f3c6

Please sign in to comment.