Compare commits
5 commits
main
...
triple-sla
Author | SHA1 | Date | |
---|---|---|---|
e2ca945569 | |||
a835635a18 | |||
ae4b29c559 | |||
cf63bce7af | |||
70f057339d |
|
@ -1026,7 +1026,10 @@ namespace ts {
|
|||
let hasDeprecatedTag = false;
|
||||
function addJSDocComment<T extends HasJSDoc>(node: T): T {
|
||||
Debug.assert(!node.jsDoc); // Should only be called once per node
|
||||
const jsDoc = mapDefined(getJSDocCommentRanges(node, sourceText), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos));
|
||||
const ranges = getJSDocCommentRanges(node, sourceText);
|
||||
const jsDoc = ranges?.every(r => isTripleSlashComment(r, sourceText))
|
||||
? JSDocParser.parseTripleSlashes(node, ranges)
|
||||
: mapDefined(ranges, comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos));
|
||||
if (jsDoc.length) node.jsDoc = jsDoc;
|
||||
if (hasDeprecatedTag) {
|
||||
hasDeprecatedTag = false;
|
||||
|
@ -7158,6 +7161,26 @@ namespace ts {
|
|||
return jsDoc ? { jsDoc, diagnostics } : undefined;
|
||||
}
|
||||
|
||||
export function parseTripleSlashes(parent: HasJSDoc, comments: CommentRange[]) {
|
||||
const saveToken = currentToken;
|
||||
const saveParseDiagnosticsLength = parseDiagnostics.length;
|
||||
const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode;
|
||||
|
||||
const comment = doInsideOfContext(NodeFlags.JSDoc, () => parseJSDocCommentWorker(comments, /*length*/ undefined));
|
||||
setParent(comment, parent);
|
||||
|
||||
if (contextFlags & NodeFlags.JavaScriptFile) {
|
||||
if (!jsDocDiagnostics) {
|
||||
jsDocDiagnostics = [];
|
||||
}
|
||||
jsDocDiagnostics.push(...parseDiagnostics);
|
||||
}
|
||||
currentToken = saveToken;
|
||||
parseDiagnostics.length = saveParseDiagnosticsLength;
|
||||
parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode;
|
||||
return comment ? [comment] : [];
|
||||
}
|
||||
|
||||
export function parseJSDocComment(parent: HasJSDoc, start: number, length: number): JSDoc | undefined {
|
||||
const saveToken = currentToken;
|
||||
const saveParseDiagnosticsLength = parseDiagnostics.length;
|
||||
|
@ -7191,8 +7214,43 @@ namespace ts {
|
|||
CallbackParameter = 1 << 2,
|
||||
}
|
||||
|
||||
function parseJSDocCommentWorker(start = 0, length: number | undefined): JSDoc | undefined {
|
||||
const content = sourceText;
|
||||
function parseJSDocCommentWorker(startOrRanges: number | CommentRange[] = 0, length: number | undefined): JSDoc | undefined {
|
||||
// const isTripleSlash = Array.isArray(startOrRanges);
|
||||
// TODO: Probably should save a boolean isTripleSlash at the beginning and make all the nested functions change their behaviour.
|
||||
const content = sourceText; // TODO: Why alias this?
|
||||
const comments: string[] = [];
|
||||
let tags: JSDocTag[];
|
||||
let tagsPos: number;
|
||||
let tagsEnd: number;
|
||||
if (Array.isArray(startOrRanges)) {
|
||||
if (!startOrRanges.length) return undefined;
|
||||
const ranges = startOrRanges;
|
||||
let currentTag: JSDocTag | undefined; // TODO: Probably can use tags
|
||||
for (const { pos: start, end } of ranges) {
|
||||
const length = end - start;
|
||||
scanner.scanRange(start + 3, length - 3, () => {
|
||||
while (nextTokenJSDoc() !== SyntaxKind.EndOfFileToken) {
|
||||
if (token() === SyntaxKind.AtToken) {
|
||||
addTag(currentTag);
|
||||
currentTag = parseTag(0);
|
||||
}
|
||||
else {
|
||||
if (currentTag) {
|
||||
// this doesn't update currentTag.end, which will cause problems later
|
||||
// I think that parseXTag will have to return unfinished tags or something.
|
||||
(currentTag as any).comment = (currentTag.comment || "") + scanner.getTokenText()
|
||||
}
|
||||
else {
|
||||
comments.push(scanner.getTokenText());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return createJSDocComment(ranges[0].pos, ranges[ranges.length - 1].end);
|
||||
}
|
||||
|
||||
const start = startOrRanges;
|
||||
const end = length === undefined ? content.length : start + length;
|
||||
length = end - start;
|
||||
|
||||
|
@ -7205,10 +7263,6 @@ namespace ts {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
let tags: JSDocTag[];
|
||||
let tagsPos: number;
|
||||
let tagsEnd: number;
|
||||
const comments: string[] = [];
|
||||
|
||||
// + 3 for leading /**, - 5 in total for /** */
|
||||
return scanner.scanRange(start + 3, length - 5, () => {
|
||||
|
@ -7292,7 +7346,7 @@ namespace ts {
|
|||
}
|
||||
removeLeadingNewlines(comments);
|
||||
removeTrailingWhitespace(comments);
|
||||
return createJSDocComment();
|
||||
return createJSDocComment(start, end);
|
||||
});
|
||||
|
||||
function removeLeadingNewlines(comments: string[]) {
|
||||
|
@ -7307,7 +7361,7 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
function createJSDocComment(): JSDoc {
|
||||
function createJSDocComment(start: number, end: number): JSDoc {
|
||||
const comment = comments.length ? comments.join("") : undefined;
|
||||
const tagsArray = tags && createNodeArray(tags, tagsPos, tagsEnd);
|
||||
return finishNode(factory.createJSDocComment(comment, tagsArray), start, end);
|
||||
|
|
|
@ -1745,6 +1745,9 @@ namespace ts {
|
|||
case CharacterCodes.slash:
|
||||
// Single-line comment
|
||||
if (text.charCodeAt(pos + 1) === CharacterCodes.slash) {
|
||||
if (text.charCodeAt(pos + 2) === CharacterCodes.slash) {
|
||||
tokenFlags |= TokenFlags.PrecedingJSDocComment;
|
||||
}
|
||||
pos += 2;
|
||||
|
||||
while (pos < end) {
|
||||
|
@ -1770,10 +1773,10 @@ namespace ts {
|
|||
}
|
||||
// Multi-line comment
|
||||
if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) {
|
||||
pos += 2;
|
||||
if (text.charCodeAt(pos) === CharacterCodes.asterisk && text.charCodeAt(pos + 1) !== CharacterCodes.slash) {
|
||||
if (text.charCodeAt(pos + 2) === CharacterCodes.asterisk && text.charCodeAt(pos + 3) !== CharacterCodes.slash) {
|
||||
tokenFlags |= TokenFlags.PrecedingJSDocComment;
|
||||
}
|
||||
pos += 2;
|
||||
|
||||
let commentClosed = false;
|
||||
let lastLineStart = tokenPos;
|
||||
|
|
|
@ -1094,6 +1094,10 @@ namespace ts {
|
|||
return node.kind !== SyntaxKind.JsxText ? getLeadingCommentRanges(sourceFileOfNode.text, node.pos) : undefined;
|
||||
}
|
||||
|
||||
export function isTripleSlashComment(comment: CommentRange, text: string) {
|
||||
return text.charCodeAt(comment.pos + 1) === CharacterCodes.slash && text.charCodeAt(comment.pos + 2) === CharacterCodes.slash;
|
||||
}
|
||||
|
||||
export function getJSDocCommentRanges(node: Node, text: string) {
|
||||
const commentRanges = (node.kind === SyntaxKind.Parameter ||
|
||||
node.kind === SyntaxKind.TypeParameter ||
|
||||
|
@ -1102,11 +1106,12 @@ namespace ts {
|
|||
node.kind === SyntaxKind.ParenthesizedExpression) ?
|
||||
concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) :
|
||||
getLeadingCommentRanges(text, node.pos);
|
||||
// True if the comment starts with '/**' but not if it is '/**/'
|
||||
// True if the comment starts with '///' or '/**' but not if it is '/**/'
|
||||
return filter(commentRanges, comment =>
|
||||
text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
|
||||
text.charCodeAt(comment.pos + 2) === CharacterCodes.asterisk &&
|
||||
text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash);
|
||||
text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash
|
||||
|| isTripleSlashComment(comment, text) && !isRecognizedTripleSlashComment(text, comment.pos, comment.end));
|
||||
}
|
||||
|
||||
export const fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
|
||||
|
|
40
tests/baselines/reference/tripleSlashJsdoc.symbols
Normal file
40
tests/baselines/reference/tripleSlashJsdoc.symbols
Normal file
|
@ -0,0 +1,40 @@
|
|||
=== tests/cases/conformance/jsdoc/tripleSlash.js ===
|
||||
/// @type {number} - TODO this is still skipped for some reason
|
||||
var x;
|
||||
>x : Symbol(x, Decl(tripleSlash.js, 1, 3))
|
||||
|
||||
/// Adds one
|
||||
/// @param {number} n - this is a long,
|
||||
/// multiline comment
|
||||
///
|
||||
/// @return {number}
|
||||
function add1(n) {
|
||||
>add1 : Symbol(add1, Decl(tripleSlash.js, 1, 6))
|
||||
>n : Symbol(n, Decl(tripleSlash.js, 8, 14))
|
||||
|
||||
return n + 1
|
||||
>n : Symbol(n, Decl(tripleSlash.js, 8, 14))
|
||||
}
|
||||
|
||||
// Should be the same
|
||||
|
||||
/** Adds one
|
||||
* @param {number} n - this is a long,
|
||||
* multiline comment
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
function add2(n) {
|
||||
>add2 : Symbol(add2, Decl(tripleSlash.js, 10, 1))
|
||||
>n : Symbol(n, Decl(tripleSlash.js, 20, 14))
|
||||
|
||||
return n + 1
|
||||
>n : Symbol(n, Decl(tripleSlash.js, 20, 14))
|
||||
}
|
||||
|
||||
/// I documented this const
|
||||
const documented = ""
|
||||
>documented : Symbol(documented, Decl(tripleSlash.js, 25, 5))
|
||||
|
||||
|
||||
|
45
tests/baselines/reference/tripleSlashJsdoc.types
Normal file
45
tests/baselines/reference/tripleSlashJsdoc.types
Normal file
|
@ -0,0 +1,45 @@
|
|||
=== tests/cases/conformance/jsdoc/tripleSlash.js ===
|
||||
/// @type {number} - TODO this is still skipped for some reason
|
||||
var x;
|
||||
>x : number
|
||||
|
||||
/// Adds one
|
||||
/// @param {number} n - this is a long,
|
||||
/// multiline comment
|
||||
///
|
||||
/// @return {number}
|
||||
function add1(n) {
|
||||
>add1 : (n: number) => number
|
||||
>n : number
|
||||
|
||||
return n + 1
|
||||
>n + 1 : number
|
||||
>n : number
|
||||
>1 : 1
|
||||
}
|
||||
|
||||
// Should be the same
|
||||
|
||||
/** Adds one
|
||||
* @param {number} n - this is a long,
|
||||
* multiline comment
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
function add2(n) {
|
||||
>add2 : (n: number) => number
|
||||
>n : number
|
||||
|
||||
return n + 1
|
||||
>n + 1 : number
|
||||
>n : number
|
||||
>1 : 1
|
||||
}
|
||||
|
||||
/// I documented this const
|
||||
const documented = ""
|
||||
>documented : ""
|
||||
>"" : ""
|
||||
|
||||
|
||||
|
34
tests/cases/conformance/jsdoc/tripleSlashJsdoc.ts
Normal file
34
tests/cases/conformance/jsdoc/tripleSlashJsdoc.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noImplicitAny: true
|
||||
// @noEmit: true
|
||||
// @filename: tripleSlash.js
|
||||
|
||||
/// @type {number} - TODO this is still skipped for some reason
|
||||
var x;
|
||||
|
||||
/// Adds one
|
||||
/// @param {number} n - this is a long,
|
||||
/// multiline comment
|
||||
///
|
||||
/// @return {number}
|
||||
function add1(n) {
|
||||
return n + 1
|
||||
}
|
||||
|
||||
// Should be the same
|
||||
|
||||
/** Adds one
|
||||
* @param {number} n - this is a long,
|
||||
* multiline comment
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
function add2(n) {
|
||||
return n + 1
|
||||
}
|
||||
|
||||
/// I documented this const
|
||||
const documented = ""
|
||||
|
||||
|
|
@ -202,7 +202,7 @@
|
|||
////class NoQuic/*50*/kInfoClass {
|
||||
////}
|
||||
|
||||
verify.signatureHelp({ marker: "1", docComment: "" });
|
||||
verify.signatureHelp({ marker: "1", docComment: " This is simple /// comments" });
|
||||
verify.quickInfoAt("1q", "function simple(): void");
|
||||
|
||||
verify.signatureHelp({ marker: "2", docComment: "" });
|
||||
|
|
18
tests/cases/fourslash/signatureHelpTripleSlashJSDoc.ts
Normal file
18
tests/cases/fourslash/signatureHelpTripleSlashJSDoc.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
|
||||
// @Filename: test.js
|
||||
//// /// Adds one
|
||||
//// /// @param {number} n - this is a long,
|
||||
//// /// multiline comment
|
||||
//// ///
|
||||
//// /// @return {number}
|
||||
//// function add1(/*2*/n) {
|
||||
//// return n + 1
|
||||
//// }
|
||||
//// add1/*1*/(12)
|
||||
|
||||
verify.quickInfoAt('1', 'function add1(n: number): number', ' Adds one ')
|
||||
verify.quickInfoAt('2', '(parameter) n: number', '- this is a long, multiline comment ')
|
Loading…
Reference in a new issue