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

Allow symbols to be escaped in DocText #3375

Merged
Merged
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"
---

Allow `@` to be escaped in doc comment with `\`
6 changes: 6 additions & 0 deletions packages/compiler/src/core/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2850,6 +2850,12 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
}
nextToken();
break;
case Token.DocText:
parts.push(source.substring(start, tokenPos()));
parts.push(tokenValue());
nextToken();
start = tokenPos();
break;
default:
nextToken();
break;
Expand Down
46 changes: 46 additions & 0 deletions packages/compiler/src/core/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ export function createScanner(
return getStringTokenValue(token, tokenFlags);
case Token.Identifier:
return getIdentifierTokenValue();
case Token.DocText:
return getDocTextValue();
default:
return getTokenText();
}
Expand Down Expand Up @@ -656,6 +658,10 @@ export function createScanner(
case CharCode.LineFeed:
return next(Token.NewLine);

case CharCode.Backslash:
tokenFlags |= TokenFlags.Escaped;
return position === endPosition - 1 ? next(Token.DocText) : next(Token.DocText, 2);

case CharCode.Space:
case CharCode.Tab:
case CharCode.VerticalTab:
Expand Down Expand Up @@ -1036,6 +1042,44 @@ export function createScanner(
return text;
}

function getDocTextValue(): string {
if (tokenFlags & TokenFlags.Escaped) {
let start = tokenPosition;
const end = position;

let result = "";
let pos = start;

while (pos < end) {
const ch = input.charCodeAt(pos);
if (ch !== CharCode.Backslash) {
pos++;
continue;
}

if (pos === end - 1) {
break;
}

result += input.substring(start, pos);
switch (input.charCodeAt(pos + 1)) {
case CharCode.At:
result += "@";
break;
default:
result += input.substring(pos, pos + 2);
}
pos += 2;
start = pos;
}

result += input.substring(start, end);
return result;
} else {
return input.substring(tokenPosition, position);
}
}

function findTripleQuotedStringIndent(start: number, end: number): [number, number] {
end = end - 3; // Remove the """
// remove whitespace before closing delimiter and record it as required
Expand Down Expand Up @@ -1231,6 +1275,8 @@ export function createScanner(
return "\\";
case CharCode.$:
return "$";
case CharCode.At:
return "@";
case CharCode.Backtick:
return "`";
default:
Expand Down
19 changes: 18 additions & 1 deletion packages/compiler/test/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,19 @@ describe("compiler: parser", () => {
strictEqual(docs[0].tags.length, 0);
},
],
[
`
/** Escape at the end \\*/
model M {}
`,
(script) => {
const docs = script.statements[0].docs;
strictEqual(docs?.length, 1);
strictEqual(docs[0].content.length, 1);
strictEqual(docs[0].content[0].text, "Escape at the end \\");
strictEqual(docs[0].tags.length, 0);
},
],
[
`
/**
Expand All @@ -1024,6 +1037,8 @@ describe("compiler: parser", () => {
*\`\`\`
*
* \`This is not a @tag either because we're in a code span\`.
*
* This is not a \\@tag because it is escaped.
*
* @param x the param
* that continues on another line
Expand Down Expand Up @@ -1052,7 +1067,9 @@ describe("compiler: parser", () => {
"This code fence is glued\n" +
"to the stars\n" +
"```\n\n" +
"`This is not a @tag either because we're in a code span`."
"`This is not a @tag either because we're in a code span`.\n" +
"\n" +
"This is not a @tag because it is escaped."
);
strictEqual(docs[0].tags.length, 6);
const [xParam, yParam, tTemplate, uTemplate, returns, pretend] = docs[0].tags;
Expand Down
Loading