diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 0b9a0142496aa..3a7adbc260915 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6481,7 +6481,25 @@ namespace ts { return finishNode(result, end); } + function isNextNonwhitespaceTokenEndOfFile(): boolean { + // We must use infinite lookahead, as there could be any number of newlines :( + while (true) { + nextJSDocToken(); + if (token() === SyntaxKind.EndOfFileToken) { + return true; + } + if (!(token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia)) { + return false; + } + } + } + function skipWhitespace(): void { + if (token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { + if (lookAhead(isNextNonwhitespaceTokenEndOfFile)) { + return; // Don't skip whitespace prior to EoF (or end of comment) - that shouldn't be included in any node's range + } + } while (token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { nextJSDocToken(); } @@ -6802,6 +6820,7 @@ namespace ts { typedefTag.comment = parseTagComments(indent); typedefTag.typeExpression = typeExpression; + let end: number; if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) { let child: JSDocTypeTag | JSDocPropertyTag | false; let jsdocTypeLiteral: JSDocTypeLiteral; @@ -6830,10 +6849,12 @@ namespace ts { typedefTag.typeExpression = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ? childTypeTag.typeExpression : finishNode(jsdocTypeLiteral); + end = typedefTag.typeExpression.end; } } - return finishNode(typedefTag); + // Only include the characters between the name end and the next token if a comment was actually parsed out - otherwise it's just whitespace + return finishNode(typedefTag, end || typedefTag.comment !== undefined ? scanner.getStartPos() : (typedefTag.fullName || typedefTag.typeExpression || typedefTag.tagName).end); } function parseJSDocTypeNameWithNamespace(nested?: boolean) { @@ -7075,7 +7096,7 @@ namespace ts { const pos = scanner.getTokenPos(); const end = scanner.getTextPos(); const result = createNode(SyntaxKind.Identifier, pos); - result.escapedText = escapeLeadingUnderscores(content.substring(pos, end)); + result.escapedText = escapeLeadingUnderscores(scanner.getTokenText()); finishNode(result, end); nextJSDocToken(); diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index f2a01d22c2480..1f0c097d22aa5 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1928,13 +1928,11 @@ namespace ts { } function scanJSDocToken(): JsDocSyntaxKind { + startPos = tokenPos = pos; if (pos >= end) { return token = SyntaxKind.EndOfFileToken; } - startPos = pos; - tokenPos = pos; - const ch = text.charCodeAt(pos); pos++; switch (ch) { diff --git a/src/services/classifier.ts b/src/services/classifier.ts index 89c96a45021b9..9719a1ccdad91 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -706,16 +706,17 @@ namespace ts { break; case SyntaxKind.JSDocTemplateTag: processJSDocTemplateTag(tag); + pos = tag.end; break; case SyntaxKind.JSDocTypeTag: processElement((tag).typeExpression); + pos = tag.end; break; case SyntaxKind.JSDocReturnTag: processElement((tag).typeExpression); + pos = tag.end; break; } - - pos = tag.end; } } diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.Nested @param tags.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.Nested @param tags.json index 03cffdc49ff37..73d3f598059dd 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.Nested @param tags.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.Nested @param tags.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 6, - "end": 63, + "end": 64, "atToken": { "kind": "AtToken", "pos": 6, @@ -21,11 +21,11 @@ "typeExpression": { "kind": "JSDocTypeExpression", "pos": 34, - "end": 63, + "end": 64, "type": { "kind": "JSDocTypeLiteral", "pos": 34, - "end": 63, + "end": 64, "jsDocPropertyTags": [ { "kind": "JSDocParameterTag", @@ -88,6 +88,6 @@ }, "length": 1, "pos": 6, - "end": 63 + "end": 64 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.argSynonymForParamTag.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.argSynonymForParamTag.json index 00d7f0dcc30b6..cde6addda7cc6 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.argSynonymForParamTag.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.argSynonymForParamTag.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 8, - "end": 40, + "end": 42, "atToken": { "kind": "AtToken", "pos": 8, @@ -40,6 +40,6 @@ }, "length": 1, "pos": 8, - "end": 40 + "end": 42 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.argumentSynonymForParamTag.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.argumentSynonymForParamTag.json index 6953601f112ca..f193bc3fe9ef6 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.argumentSynonymForParamTag.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.argumentSynonymForParamTag.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 8, - "end": 45, + "end": 47, "atToken": { "kind": "AtToken", "pos": 8, @@ -40,6 +40,6 @@ }, "length": 1, "pos": 8, - "end": 45 + "end": 47 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.less-than and greater-than characters.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.less-than and greater-than characters.json index 472fbbeb6bb86..37d4610f9873c 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.less-than and greater-than characters.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.less-than and greater-than characters.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 7, - "end": 58, + "end": 59, "atToken": { "kind": "AtToken", "pos": 7, @@ -30,6 +30,6 @@ }, "length": 1, "pos": 7, - "end": 58 + "end": 59 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noReturnType.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noReturnType.json index 079d09c6eeb56..204ba39d3dd0b 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noReturnType.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noReturnType.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocReturnTag", "pos": 8, - "end": 16, + "end": 15, "atToken": { "kind": "AtToken", "pos": 8, @@ -21,6 +21,6 @@ }, "length": 1, "pos": 8, - "end": 16 + "end": 15 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.oneParamTag.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.oneParamTag.json index 4940bcf325ebc..f5eee243cf57e 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.oneParamTag.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.oneParamTag.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 8, - "end": 30, + "end": 32, "atToken": { "kind": "AtToken", "pos": 8, @@ -39,6 +39,6 @@ }, "length": 1, "pos": 8, - "end": 30 + "end": 32 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTag1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTag1.json index b3e58d84923c6..cbbb64b5a73d3 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTag1.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTag1.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 8, - "end": 55, + "end": 57, "atToken": { "kind": "AtToken", "pos": 8, @@ -40,6 +40,6 @@ }, "length": 1, "pos": 8, - "end": 55 + "end": 57 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName1.json index 6721afb2ea74c..a27e0d158e2b0 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName1.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName1.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 8, - "end": 57, + "end": 59, "atToken": { "kind": "AtToken", "pos": 8, @@ -40,6 +40,6 @@ }, "length": 1, "pos": 8, - "end": 57 + "end": 59 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName2.json index bf53423ad6a27..d271a3b348373 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName2.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName2.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 8, - "end": 62, + "end": 64, "atToken": { "kind": "AtToken", "pos": 8, @@ -40,6 +40,6 @@ }, "length": 1, "pos": 8, - "end": 62 + "end": 64 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType2.json index 68edeb9019081..57ab44a68b7db 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType2.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType2.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 8, - "end": 42, + "end": 44, "atToken": { "kind": "AtToken", "pos": 8, @@ -40,6 +40,6 @@ }, "length": 1, "pos": 8, - "end": 42 + "end": 44 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramWithoutType.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramWithoutType.json index 3d511525c643f..e85d787cd9975 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramWithoutType.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramWithoutType.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocParameterTag", "pos": 8, - "end": 19, + "end": 21, "atToken": { "kind": "AtToken", "pos": 8, @@ -29,6 +29,6 @@ }, "length": 1, "pos": 8, - "end": 19 + "end": 21 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag.json index 8a146e3cfaf2c..4d16157d91d91 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocTemplateTag", "pos": 8, - "end": 20, + "end": 19, "atToken": { "kind": "AtToken", "pos": 8, @@ -22,7 +22,7 @@ "0": { "kind": "TypeParameter", "pos": 18, - "end": 20, + "end": 19, "name": { "kind": "Identifier", "pos": 18, @@ -32,11 +32,11 @@ }, "length": 1, "pos": 18, - "end": 20 + "end": 19 } }, "length": 1, "pos": 8, - "end": 20 + "end": 19 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag2.json index 5bb1df306651c..3f5f2a54ec7c4 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag2.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag2.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocTemplateTag", "pos": 8, - "end": 22, + "end": 21, "atToken": { "kind": "AtToken", "pos": 8, @@ -33,7 +33,7 @@ "1": { "kind": "TypeParameter", "pos": 20, - "end": 22, + "end": 21, "name": { "kind": "Identifier", "pos": 20, @@ -43,11 +43,11 @@ }, "length": 2, "pos": 18, - "end": 22 + "end": 21 } }, "length": 1, "pos": 8, - "end": 22 + "end": 21 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json index 295b2122daab2..ab1b9db87822e 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocTemplateTag", "pos": 8, - "end": 23, + "end": 22, "atToken": { "kind": "AtToken", "pos": 8, @@ -33,7 +33,7 @@ "1": { "kind": "TypeParameter", "pos": 21, - "end": 23, + "end": 22, "name": { "kind": "Identifier", "pos": 21, @@ -43,11 +43,11 @@ }, "length": 2, "pos": 18, - "end": 23 + "end": 22 } }, "length": 1, "pos": 8, - "end": 23 + "end": 22 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag4.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag4.json index 4aa29db309216..193c5c0eb0141 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag4.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag4.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocTemplateTag", "pos": 8, - "end": 23, + "end": 22, "atToken": { "kind": "AtToken", "pos": 8, @@ -33,7 +33,7 @@ "1": { "kind": "TypeParameter", "pos": 21, - "end": 23, + "end": 22, "name": { "kind": "Identifier", "pos": 21, @@ -43,11 +43,11 @@ }, "length": 2, "pos": 18, - "end": 23 + "end": 22 } }, "length": 1, "pos": 8, - "end": 23 + "end": 22 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json index 5e707f6f03b94..80e127b07592f 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocTemplateTag", "pos": 8, - "end": 24, + "end": 23, "atToken": { "kind": "AtToken", "pos": 8, @@ -33,7 +33,7 @@ "1": { "kind": "TypeParameter", "pos": 22, - "end": 24, + "end": 23, "name": { "kind": "Identifier", "pos": 22, @@ -43,11 +43,11 @@ }, "length": 2, "pos": 18, - "end": 24 + "end": 23 } }, "length": 1, "pos": 8, - "end": 24 + "end": 23 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTag2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTag2.json index 6f073802fcdc1..3a5e71ef8b1dc 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTag2.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTag2.json @@ -40,7 +40,7 @@ "1": { "kind": "JSDocParameterTag", "pos": 34, - "end": 56, + "end": 58, "atToken": { "kind": "AtToken", "pos": 34, @@ -73,6 +73,6 @@ }, "length": 2, "pos": 8, - "end": 56 + "end": 58 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTagOnSameLine.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTagOnSameLine.json index e1ef0adb9263d..51868df260b90 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTagOnSameLine.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTagOnSameLine.json @@ -40,7 +40,7 @@ "1": { "kind": "JSDocParameterTag", "pos": 30, - "end": 52, + "end": 54, "atToken": { "kind": "AtToken", "pos": 30, @@ -73,6 +73,6 @@ }, "length": 2, "pos": 8, - "end": 52 + "end": 54 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json index 98a59931ad8a5..cf523b71c99c9 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json @@ -6,7 +6,7 @@ "0": { "kind": "JSDocTypedefTag", "pos": 8, - "end": 98, + "end": 100, "atToken": { "kind": "AtToken", "pos": 8, @@ -33,7 +33,7 @@ "typeExpression": { "kind": "JSDocTypeLiteral", "pos": 28, - "end": 98, + "end": 100, "jsDocPropertyTags": [ { "kind": "JSDocPropertyTag", @@ -72,7 +72,7 @@ { "kind": "JSDocPropertyTag", "pos": 74, - "end": 98, + "end": 97, "atToken": { "kind": "AtToken", "pos": 74, @@ -108,6 +108,6 @@ }, "length": 1, "pos": 8, - "end": 98 + "end": 100 } } \ No newline at end of file diff --git a/tests/cases/fourslash/incrementalJsDocAdjustsLengthsRight.ts b/tests/cases/fourslash/incrementalJsDocAdjustsLengthsRight.ts new file mode 100644 index 0000000000000..626a1e372588a --- /dev/null +++ b/tests/cases/fourslash/incrementalJsDocAdjustsLengthsRight.ts @@ -0,0 +1,33 @@ +/// + +// @noLib: true +//// +/////** +//// * Pad `str` to `width`. +//// * +//// * @param {String} str +//// * @param {Number} wid/*1*/ +goTo.marker('1'); +edit.insert("th\n@"); +const c = classification; +verify.syntacticClassificationsAre( + c.comment("/**\n * Pad `str` to `width`.\n *\n * "), + c.punctuation("@"), + c.docCommentTagName("param"), + c.comment(" "), + c.punctuation("{"), + c.identifier("String"), + c.punctuation("}"), + c.comment(" "), + c.parameterName("str"), + c.comment("\n * "), + c.punctuation("@"), + c.docCommentTagName("param"), + c.comment(" "), + c.punctuation("{"), + c.identifier("Number"), + c.punctuation("}"), + c.comment(" "), + c.parameterName("wid"), + c.comment(""), // syntatic classification verification always just uses input text, so the edits don't appear +);