Skip to content

Commit 128e043

Browse files
committed
Use the new JSDoc parser to handle @param tags
Fixes microsoft#6810
1 parent 554ea1b commit 128e043

File tree

4 files changed

+113
-164
lines changed

4 files changed

+113
-164
lines changed

src/services/services.ts

Lines changed: 81 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,68 @@ namespace ts {
375375

376376
return documentationComment;
377377

378+
function getCommentFromJsDocTag(tag: JSDocTag, rangeEnd: number, sourceFile: SourceFile) {
379+
const text = sourceFile.text;
380+
381+
const parts: string[] = [];
382+
let start = tag.end + 1;
383+
384+
// Eat any leading ignored characters
385+
while (start < rangeEnd) {
386+
const ch = text.charCodeAt(start);
387+
if (ch === CharacterCodes.asterisk || isWhiteSpace(ch)) {
388+
start++;
389+
}
390+
else if (ch === CharacterCodes.at) {
391+
// Most likely a malformed tag
392+
return undefined;
393+
}
394+
else {
395+
break;
396+
}
397+
}
398+
399+
let i: number;
400+
for (i = start; i < rangeEnd; i++) {
401+
const ch = text.charCodeAt(i);
402+
if (isLineBreak(ch)) {
403+
// Line break, push any non-empty content so far
404+
if (start !== -1 && start !== i) {
405+
parts.push(text.substr(start, i - start));
406+
}
407+
408+
// Keep scanning until we hit either a a non-ws non-* char
409+
while (i < rangeEnd) {
410+
i++;
411+
const nextCh = text.charCodeAt(i);
412+
if ((nextCh === CharacterCodes.asterisk) || (isWhiteSpace(nextCh) || isLineBreak(nextCh))) {
413+
// Eat *s and whitespace
414+
}
415+
else if (nextCh === CharacterCodes.at) {
416+
// Abort, we're running into another malformed jsdoc tag
417+
i = rangeEnd;
418+
break;
419+
}
420+
else {
421+
// Scan this
422+
break;
423+
}
424+
}
425+
start = i;
426+
}
427+
}
428+
429+
if (start !== -1 && start < rangeEnd) {
430+
parts.push(text.substr(start, rangeEnd - start));
431+
}
432+
433+
const result: SymbolDisplayPart = {
434+
kind: "documentation",
435+
text: parts.join("\n")
436+
};
437+
return result;
438+
}
439+
378440
function getJsDocCommentsSeparatedByNewLines() {
379441
const paramTag = "@param";
380442
const jsDocCommentParts: SymbolDisplayPart[] = [];
@@ -391,9 +453,25 @@ namespace ts {
391453
// If it is parameter - try and get the jsDoc comment with @param tag from function declaration's jsDoc comments
392454
if (canUseParsedParamTagComments && declaration.kind === SyntaxKind.Parameter) {
393455
ts.forEach(getJsDocCommentTextRange(declaration.parent, sourceFileOfDeclaration), jsDocCommentTextRange => {
394-
const cleanedParamJsDocComment = getCleanedParamJsDocComment(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration);
395-
if (cleanedParamJsDocComment) {
396-
addRange(jsDocCommentParts, cleanedParamJsDocComment);
456+
// Incoming function strips off leading /* and trailing */, so add those back (-2, +4 net)
457+
const content = parseIsolatedJSDocComment(sourceFileOfDeclaration.text, jsDocCommentTextRange.pos - 2, jsDocCommentTextRange.end - jsDocCommentTextRange.pos + 4);
458+
if (content && content.jsDocComment.tags) {
459+
const tags = content.jsDocComment.tags;
460+
461+
for (let i = 0; i < tags.length; i++) {
462+
const tag = tags[i];
463+
if (tag.kind === SyntaxKind.JSDocParameterTag) {
464+
const tagParamName = (tag as JSDocParameterTag).preParameterName || (tag as JSDocParameterTag).postParameterName;
465+
// -2 because we don't want to see the trailing */
466+
const nextTagStart = (i === tags.length - 1) ? jsDocCommentTextRange.end : tags[i + 1].pos;
467+
if (tagParamName && tagParamName.text === name) {
468+
const comment = getCommentFromJsDocTag(tag, nextTagStart, sourceFileOfDeclaration);
469+
if (comment) {
470+
jsDocCommentParts.push(comment);
471+
}
472+
}
473+
}
474+
}
397475
}
398476
});
399477
}
@@ -540,155 +618,6 @@ namespace ts {
540618

541619
return docComments;
542620
}
543-
544-
function getCleanedParamJsDocComment(pos: number, end: number, sourceFile: SourceFile) {
545-
let paramHelpStringMargin: number;
546-
const paramDocComments: SymbolDisplayPart[] = [];
547-
while (pos < end) {
548-
if (isParamTag(pos, end, sourceFile)) {
549-
let blankLineCount = 0;
550-
let recordedParamTag = false;
551-
// Consume leading spaces
552-
pos = consumeWhiteSpaces(pos + paramTag.length);
553-
if (pos >= end) {
554-
break;
555-
}
556-
557-
// Ignore type expression
558-
if (sourceFile.text.charCodeAt(pos) === CharacterCodes.openBrace) {
559-
pos++;
560-
for (let curlies = 1; pos < end; pos++) {
561-
const charCode = sourceFile.text.charCodeAt(pos);
562-
563-
// { character means we need to find another } to match the found one
564-
if (charCode === CharacterCodes.openBrace) {
565-
curlies++;
566-
continue;
567-
}
568-
569-
// } char
570-
if (charCode === CharacterCodes.closeBrace) {
571-
curlies--;
572-
if (curlies === 0) {
573-
// We do not have any more } to match the type expression is ignored completely
574-
pos++;
575-
break;
576-
}
577-
else {
578-
// there are more { to be matched with }
579-
continue;
580-
}
581-
}
582-
583-
// Found start of another tag
584-
if (charCode === CharacterCodes.at) {
585-
break;
586-
}
587-
}
588-
589-
// Consume white spaces
590-
pos = consumeWhiteSpaces(pos);
591-
if (pos >= end) {
592-
break;
593-
}
594-
}
595-
596-
// Parameter name
597-
if (isName(pos, end, sourceFile, name)) {
598-
// Found the parameter we are looking for consume white spaces
599-
pos = consumeWhiteSpaces(pos + name.length);
600-
if (pos >= end) {
601-
break;
602-
}
603-
604-
let paramHelpString = "";
605-
const firstLineParamHelpStringPos = pos;
606-
while (pos < end) {
607-
const ch = sourceFile.text.charCodeAt(pos);
608-
609-
// at line break, set this comment line text and go to next line
610-
if (isLineBreak(ch)) {
611-
if (paramHelpString) {
612-
pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount);
613-
paramHelpString = "";
614-
blankLineCount = 0;
615-
recordedParamTag = true;
616-
}
617-
else if (recordedParamTag) {
618-
blankLineCount++;
619-
}
620-
621-
// Get the pos after cleaning start of the line
622-
setPosForParamHelpStringOnNextLine(firstLineParamHelpStringPos);
623-
continue;
624-
}
625-
626-
// Done scanning param help string - next tag found
627-
if (ch === CharacterCodes.at) {
628-
break;
629-
}
630-
631-
paramHelpString += sourceFile.text.charAt(pos);
632-
633-
// Go to next character
634-
pos++;
635-
}
636-
637-
// If there is param help text, add it top the doc comments
638-
if (paramHelpString) {
639-
pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount);
640-
}
641-
paramHelpStringMargin = undefined;
642-
}
643-
644-
// If this is the start of another tag, continue with the loop in seach of param tag with symbol name
645-
if (sourceFile.text.charCodeAt(pos) === CharacterCodes.at) {
646-
continue;
647-
}
648-
}
649-
650-
// Next character
651-
pos++;
652-
}
653-
654-
return paramDocComments;
655-
656-
function consumeWhiteSpaces(pos: number) {
657-
while (pos < end && isWhiteSpace(sourceFile.text.charCodeAt(pos))) {
658-
pos++;
659-
}
660-
661-
return pos;
662-
}
663-
664-
function setPosForParamHelpStringOnNextLine(firstLineParamHelpStringPos: number) {
665-
// Get the pos after consuming line breaks
666-
pos = consumeLineBreaks(pos, end, sourceFile);
667-
if (pos >= end) {
668-
return;
669-
}
670-
671-
if (paramHelpStringMargin === undefined) {
672-
paramHelpStringMargin = sourceFile.getLineAndCharacterOfPosition(firstLineParamHelpStringPos).character;
673-
}
674-
675-
// Now consume white spaces max
676-
const startOfLinePos = pos;
677-
pos = consumeWhiteSpacesOnTheLine(pos, end, sourceFile, paramHelpStringMargin);
678-
if (pos >= end) {
679-
return;
680-
}
681-
682-
const consumedSpaces = pos - startOfLinePos;
683-
if (consumedSpaces < paramHelpStringMargin) {
684-
const ch = sourceFile.text.charCodeAt(pos);
685-
if (ch === CharacterCodes.asterisk) {
686-
// Consume more spaces after asterisk
687-
pos = consumeWhiteSpacesOnTheLine(pos + 1, end, sourceFile, paramHelpStringMargin - consumedSpaces - 1);
688-
}
689-
}
690-
}
691-
}
692621
}
693622
}
694623

tests/cases/fourslash/commentsCommentParsing.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,9 @@ verify.quickInfoIs("(parameter) b: number", "");
310310

311311
goTo.marker('21');
312312
verify.currentSignatureHelpDocCommentIs("This is multiplication function\n@anotherTag\n@anotherTag");
313-
verify.currentParameterHelpArgumentDocCommentIs("{");
313+
verify.currentParameterHelpArgumentDocCommentIs("");
314314
goTo.marker('21aq');
315-
verify.quickInfoIs("(parameter) c: number", "{");
315+
verify.quickInfoIs("(parameter) c: number", "");
316316

317317
goTo.marker('22');
318318
verify.currentSignatureHelpDocCommentIs("This is multiplication function\n@anotherTag\n@anotherTag");
@@ -372,9 +372,9 @@ verify.quickInfoIs("(parameter) c: () => string", "this is optional param c");
372372

373373
goTo.marker('31');
374374
verify.currentSignatureHelpDocCommentIs("This is subtract function");
375-
verify.currentParameterHelpArgumentDocCommentIs("");
375+
verify.currentParameterHelpArgumentDocCommentIs("this is optional param d");
376376
goTo.marker('31aq');
377-
verify.quickInfoIs("(parameter) d: () => string", "");
377+
verify.quickInfoIs("(parameter) d: () => string", "this is optional param d");
378378

379379
goTo.marker('32');
380380
verify.currentSignatureHelpDocCommentIs("This is subtract function");
@@ -384,9 +384,9 @@ verify.quickInfoIs("(parameter) e: () => string", "this is optional param e");
384384

385385
goTo.marker('33');
386386
verify.currentSignatureHelpDocCommentIs("This is subtract function");
387-
verify.currentParameterHelpArgumentDocCommentIs("");
387+
verify.currentParameterHelpArgumentDocCommentIs("this is optional param f");
388388
goTo.marker('33aq');
389-
verify.quickInfoIs("(parameter) f: () => string", "");
389+
verify.quickInfoIs("(parameter) f: () => string", "this is optional param f");
390390

391391
goTo.marker('34');
392392
verify.currentSignatureHelpDocCommentIs("this is square function\n@paramTag { number } a this is input number of paramTag\n@returnType { number } it is return type");
@@ -481,9 +481,9 @@ verify.quickInfoIs("(parameter) a: string", "this is info about a\nspanning on t
481481

482482
goTo.marker('48');
483483
verify.currentSignatureHelpDocCommentIs("This is function comment\n And aligned with 4 space char margin");
484-
verify.currentParameterHelpArgumentDocCommentIs("this is info about b\nspanning on two lines and aligned perfectly\nspanning one more line alined perfectly\n spanning another line with more margin");
484+
verify.currentParameterHelpArgumentDocCommentIs("this is info about b\nspanning on two lines and aligned perfectly\nspanning one more line alined perfectly\nspanning another line with more margin");
485485
goTo.marker('48aq');
486-
verify.quickInfoIs("(parameter) b: any", "this is info about b\nspanning on two lines and aligned perfectly\nspanning one more line alined perfectly\n spanning another line with more margin");
486+
verify.quickInfoIs("(parameter) b: any", "this is info about b\nspanning on two lines and aligned perfectly\nspanning one more line alined perfectly\nspanning another line with more margin");
487487

488488
goTo.marker('49');
489489
verify.currentSignatureHelpDocCommentIs("This is function comment\n And aligned with 4 space char margin");

tests/cases/fourslash/commentsLinePreservation.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ verify.quickInfoIs(undefined, "");
128128
goTo.marker('f');
129129
verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n@random tag This should be third line");
130130
goTo.marker('3');
131-
verify.quickInfoIs(undefined, "first line of param\n\nparam information third line");
131+
verify.quickInfoIs(undefined, "first line of param\nparam information third line");
132132

133133
goTo.marker('g');
134134
verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n@random tag This should be third line");
@@ -138,17 +138,17 @@ verify.quickInfoIs(undefined, "param information first line");
138138
goTo.marker('h');
139139
verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n@random tag This should be third line");
140140
goTo.marker('5');
141-
verify.quickInfoIs(undefined, "param information first line\n\nparam information third line");
141+
verify.quickInfoIs(undefined, "param information first line\nparam information third line");
142142

143143
goTo.marker('i');
144144
verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line");
145145
goTo.marker('6');
146-
verify.quickInfoIs(undefined, "param information first line\n\nparam information third line");
146+
verify.quickInfoIs(undefined, "param information first line\nparam information third line");
147147

148148
goTo.marker('j');
149149
verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line");
150150
goTo.marker('7');
151-
verify.quickInfoIs(undefined, "param information first line\n\nparam information third line");
151+
verify.quickInfoIs(undefined, "param information first line\nparam information third line");
152152

153153
goTo.marker('k');
154154
verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n@randomtag \n\n random information first line\n\n random information third line");
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
///<reference path="fourslash.ts" />
2+
3+
// @allowNonTsExtensions: true
4+
// @Filename: Foo.js
5+
6+
//// /**
7+
//// * @param {string} p1 parameter 1
8+
//// * @param {string?} p2 parameter 2
9+
//// * @param {string} [p3] parameter 3
10+
//// * @param {string} [p4="test"] parameter 4
11+
//// */
12+
//// function f1(p1, p2, p3, p4) {
13+
//// }
14+
//// f1(''/*1*/, ''/*2*/, ''/*3*/, ''/*4*/)
15+
16+
17+
for(const m of ['1', '2', '3', '4']) {
18+
goTo.marker(m);
19+
verify.currentParameterHelpArgumentDocCommentIs('parameter ' + m);
20+
}

0 commit comments

Comments
 (0)