Skip to content

Commit

Permalink
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 21 deletions.
8 changes: 8 additions & 0 deletions .chronus/changes/feature-prop-doc-2024-5-5-21-1-58.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: feature
packages:
- "@typespec/compiler"
---

Add support for `@prop` doc comment tag to describe model properties
2 changes: 1 addition & 1 deletion grammars/typespec.json
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@
},
"doc-comment-param": {
"name": "comment.block.tsp",
"match": "(?x)((@)(?:param|template))\\s+(\\b[_$[:alpha:]][_$[:alnum:]]*\\b|`(?:[^`\\\\]|\\\\.)*`)\\b",
"match": "(?x)((@)(?:param|template|prop))\\s+(\\b[_$[:alpha:]][_$[:alnum:]]*\\b|`(?:[^`\\\\]|\\\\.)*`)\\b",
"captures": {
"1": {
"name": "keyword.tag.tspdoc"
Expand Down
58 changes: 42 additions & 16 deletions packages/compiler/src/core/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2501,8 +2501,9 @@ export function createChecker(program: Program): Checker {

if (parameterModelSym?.members) {
const members = getOrCreateAugmentedSymbolTable(parameterModelSym.members);
const paramDocs = extractParamDocs(node);
for (const [name, memberSym] of members) {
const doc = extractParamDoc(node, name);
const doc = paramDocs[name];
if (doc) {
docFromCommentForSym.set(memberSym, doc);
}
Expand Down Expand Up @@ -3844,6 +3845,18 @@ export function createChecker(program: Program): Checker {
derivedModels: [],
});
linkType(links, type, mapper);

if (node.symbol.members) {
const members = getOrCreateAugmentedSymbolTable(node.symbol.members);
const propDocs = extractPropDocs(node);
for (const [name, memberSym] of members) {
const doc = propDocs[name];
if (doc) {
docFromCommentForSym.set(memberSym, doc);
}
}
}

const isBase = checkModelIs(node, node.is, mapper);

if (isBase) {
Expand All @@ -3859,7 +3872,8 @@ export function createChecker(program: Program): Checker {

if (isBase) {
for (const prop of isBase.properties.values()) {
const newProp = cloneType(prop, {
const memberSym = getMemberSymbol(node.symbol, prop.name)!;
const newProp = cloneTypeForSymbol(memberSym, prop, {
sourceProperty: prop,
model: type,
});
Expand Down Expand Up @@ -5125,7 +5139,8 @@ export function createChecker(program: Program): Checker {
prop: ModelPropertyNode,
mapper: TypeMapper | undefined
): ModelProperty {
const symId = getSymbolId(getSymbolForMember(prop)!);
const sym = getSymbolForMember(prop)!;
const symId = getSymbolId(sym);
const links = getSymbolLinksForMember(prop);

if (links && links.declaredType && mapper === undefined) {
Expand Down Expand Up @@ -5172,14 +5187,9 @@ export function createChecker(program: Program): Checker {
linkMapper(type, mapper);

if (!parentTemplate || shouldCreateTypeForTemplate(parentTemplate, mapper)) {
if (
prop.parent?.parent?.kind === SyntaxKind.OperationSignatureDeclaration &&
prop.parent.parent.parent?.kind === SyntaxKind.OperationStatement
) {
const doc = extractParamDoc(prop.parent.parent.parent, type.name);
if (doc) {
type.decorators.unshift(createDocFromCommentDecorator("self", doc));
}
const docComment = docFromCommentForSym.get(sym);
if (docComment) {
type.decorators.unshift(createDocFromCommentDecorator("self", docComment));
}
finishType(type);
}
Expand Down Expand Up @@ -8442,18 +8452,34 @@ function extractReturnsDocs(type: Type): {
return result;
}

function extractParamDoc(node: OperationStatementNode, paramName: string): string | undefined {
function extractParamDocs(node: OperationStatementNode): Record<string, string> {
if (node.docs === undefined) {
return undefined;
return {};
}
const paramDocs: Record<string, string> = {};
for (const doc of node.docs) {
for (const tag of doc.tags) {
if (tag.kind === SyntaxKind.DocParamTag && tag.paramName.sv === paramName) {
return getDocContent(tag.content);
if (tag.kind === SyntaxKind.DocParamTag) {
paramDocs[tag.paramName.sv] = getDocContent(tag.content);
}
}
}
return undefined;
return paramDocs;
}

function extractPropDocs(node: ModelStatementNode): Record<string, string> {
if (node.docs === undefined) {
return {};
}
const propDocs: Record<string, string> = {};
for (const doc of node.docs) {
for (const tag of doc.tags) {
if (tag.kind === SyntaxKind.DocPropTag) {
propDocs[tag.propName.sv] = getDocContent(tag.content);
}
}
}
return propDocs;
}

function getDocContent(content: readonly DocContent[]) {
Expand Down
1 change: 1 addition & 0 deletions packages/compiler/src/core/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ const diagnostics = {
default: "Invalid identifier.",
tag: "Invalid tag name. Use backticks around code if this was not meant to be a tag.",
param: "Invalid parameter name.",
prop: "Invalid property name.",
templateParam: "Invalid template parameter name.",
},
},
Expand Down
33 changes: 30 additions & 3 deletions packages/compiler/src/core/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ import {
DocErrorsTagNode,
DocNode,
DocParamTagNode,
DocPropTagNode,
DocReturnsTagNode,
DocTag,
DocTemplateTagNode,
DocTextNode,
DocUnknownTagNode,
EmptyStatementNode,
EnumMemberNode,
Expand Down Expand Up @@ -2891,6 +2893,8 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
return parseDocParamLikeTag(pos, tagName, SyntaxKind.DocParamTag, "param");
case "template":
return parseDocParamLikeTag(pos, tagName, SyntaxKind.DocTemplateTag, "templateParam");
case "prop":
return parseDocPropTag(pos, tagName);
case "return":
case "returns":
return parseDocSimpleTag(pos, tagName, SyntaxKind.DocReturnsTag);
Expand All @@ -2911,9 +2915,7 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
kind: ParamLikeTag["kind"],
messageId: keyof CompilerDiagnostics["doc-invalid-identifier"]
): ParamLikeTag {
const name = parseDocIdentifier(messageId);
parseOptionalHyphenDocParamLikeTag();
const content = parseDocContent();
const { name, content } = parseDocParamLikeTagInternal(messageId);

return {
kind,
Expand All @@ -2924,6 +2926,27 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
};
}

function parseDocPropTag(pos: number, tagName: IdentifierNode): DocPropTagNode {
const { name, content } = parseDocParamLikeTagInternal("prop");

return {
kind: SyntaxKind.DocPropTag,
tagName,
propName: name,
content,
...finishNode(pos),
};
}

function parseDocParamLikeTagInternal(
messageId: keyof CompilerDiagnostics["doc-invalid-identifier"]
): { name: IdentifierNode; content: DocTextNode[] } {
const name = parseDocIdentifier(messageId);
parseOptionalHyphenDocParamLikeTag();
const content = parseDocContent();
return { name, content };
}

/**
* Handles the optional hyphen in param-like documentation comment tags.
*
Expand Down Expand Up @@ -3669,6 +3692,10 @@ export function visitChildren<T>(node: Node, cb: NodeCallback<T>): T | undefined
return (
visitNode(cb, node.tagName) || visitNode(cb, node.paramName) || visitEach(cb, node.content)
);
case SyntaxKind.DocPropTag:
return (
visitNode(cb, node.tagName) || visitNode(cb, node.propName) || visitEach(cb, node.content)
);
case SyntaxKind.DocReturnsTag:
case SyntaxKind.DocErrorsTag:
case SyntaxKind.DocUnknownTag:
Expand Down
7 changes: 7 additions & 0 deletions packages/compiler/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,7 @@ export enum SyntaxKind {
Doc,
DocText,
DocParamTag,
DocPropTag,
DocReturnsTag,
DocErrorsTag,
DocTemplateTag,
Expand Down Expand Up @@ -1903,6 +1904,7 @@ export type DocTag =
| DocReturnsTagNode
| DocErrorsTagNode
| DocParamTagNode
| DocPropTagNode
| DocTemplateTagNode
| DocUnknownTagNode;
export type DocContent = DocTextNode;
Expand All @@ -1925,6 +1927,11 @@ export interface DocParamTagNode extends DocTagBaseNode {
readonly paramName: IdentifierNode;
}

export interface DocPropTagNode extends DocTagBaseNode {
readonly kind: SyntaxKind.DocPropTag;
readonly propName: IdentifierNode;
}

export interface DocTemplateTagNode extends DocTagBaseNode {
readonly kind: SyntaxKind.DocTemplateTag;
readonly paramName: IdentifierNode;
Expand Down
1 change: 1 addition & 0 deletions packages/compiler/src/formatter/print/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ export function printNode(
return printDoc(path as AstPath<DocNode>, options, print);
case SyntaxKind.DocText:
case SyntaxKind.DocParamTag:
case SyntaxKind.DocPropTag:
case SyntaxKind.DocTemplateTag:
case SyntaxKind.DocReturnsTag:
case SyntaxKind.DocErrorsTag:
Expand Down
4 changes: 4 additions & 0 deletions packages/compiler/src/server/classify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ export function getSemanticTokens(ast: TypeSpecScriptNode): SemanticToken[] {
classifyDocTag(node.tagName, SemanticTokenKind.DocCommentTag);
classifyOverride(node.paramName, SemanticTokenKind.Variable);
break;
case SyntaxKind.DocPropTag:
classifyDocTag(node.tagName, SemanticTokenKind.DocCommentTag);
classifyOverride(node.propName, SemanticTokenKind.Variable);
break;
case SyntaxKind.DocReturnsTag:
classifyDocTag(node.tagName, SemanticTokenKind.DocCommentTag);
break;
Expand Down
4 changes: 3 additions & 1 deletion packages/compiler/src/server/tmlanguage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,14 @@ const blockComment: BeginEndRule = {
const docCommentParam: MatchRule = {
key: "doc-comment-param",
scope: "comment.block.tsp",
match: `(?x)((@)(?:param|template))\\s+(${identifier})\\b`,
match: `(?x)((@)(?:param|template|prop))\\s+(${identifier})\\b`,
captures: {
"1": { scope: "keyword.tag.tspdoc" },
"2": { scope: "keyword.tag.tspdoc" },
"3": { scope: "variable.name.tsp" },
},
};

const docCommentReturn: MatchRule = {
key: "doc-comment-return-tag",
scope: "comment.block.tsp",
Expand All @@ -207,6 +208,7 @@ const docCommentReturn: MatchRule = {
"2": { scope: "keyword.tag.tspdoc" },
},
};

const docCommentUnknownTag: MatchRule = {
key: "doc-comment-unknown-tag",
scope: "comment.block.tsp",
Expand Down
Loading

0 comments on commit 7912e8c

Please sign in to comment.