Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for operation templates and operation signature reuse #552

Merged
merged 20 commits into from
Jun 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7b59ff3
Add support for operation templates and operation signature reuse
daviwil May 19, 2022
ea13b94
Replace `OperationInstanceNode` with `OperationSignature` types
daviwil May 25, 2022
96072e5
Add Rush change file
daviwil May 25, 2022
0b4307f
Skip templated operations when scanning for operation routes
daviwil May 25, 2022
48b7c6b
Add operation signatures sample
daviwil May 25, 2022
4fa104c
Fix formatter output for templated operation references
daviwil May 25, 2022
539e4fe
Create a scope for operation statements to bind template parameters
daviwil May 25, 2022
2f3dc0b
Add another operation signature to the signatures sample
daviwil May 25, 2022
c7e9ed6
Add an example of operation signatures used in a templated interface
daviwil May 25, 2022
868ce0c
Update language grammar specification for new syntax
daviwil May 25, 2022
073838d
Format signatures.cadl
daviwil May 25, 2022
f5618c2
Convert `OperationSignature` types to real syntax nodes
daviwil May 25, 2022
1ddf64e
Use `expectTokenIsOneOf`
daviwil May 25, 2022
78a9ebd
Update `InterfaceMember` in language specification
daviwil May 25, 2022
96a2994
Use `op is Sig` syntax instead of previous `op: Sig` syntax
daviwil Jun 1, 2022
c00cdd8
Non-working updates to tmLanguage grammar
daviwil Jun 2, 2022
aacfcc1
Nick's suggested changes
daviwil Jun 2, 2022
c0dc596
Update tmLanguage and tests
daviwil Jun 2, 2022
7e8c57e
Add a tmLanguage test for templated operation signatures
daviwil Jun 2, 2022
ebde6be
Add Rush change file for vscode extension
daviwil Jun 2, 2022
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,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();
timotheeguerin marked this conversation as resolved.
Show resolved Hide resolved
}
}

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