diff --git a/src/services/classifier.ts b/src/services/classifier.ts index f85a69681de..f1db477481f 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -683,6 +683,11 @@ namespace ts { return; } } + else if (kind === SyntaxKind.SingleLineCommentTrivia) { + if (tryClassifyTripleSlashComment(start, width)) { + return; + } + } // Simple comment. Just add as is. pushCommentRange(start, width); @@ -755,6 +760,84 @@ namespace ts { } } + function tryClassifyTripleSlashComment(start: number, width: number): boolean { + const tripleSlashXMLCommentRegEx = /^(\/\/\/\s*)(<)(?:(\S+)((?:[^/]|\/[^>])*)(\/>)?)?/im; + const attributeRegex = /(\S+)(\s*)(=)(\s*)('[^']+'|"[^"]+")/img; + + const text = sourceFile.text.substr(start, width); + const match = tripleSlashXMLCommentRegEx.exec(text); + if (!match) { + return false; + } + + let pos = start; + + pushCommentRange(pos, match[1].length); // /// + pos += match[1].length; + + pushClassification(pos, match[2].length, ClassificationType.punctuation); // < + pos += match[2].length; + + if (!match[3]) { + return true; + } + + pushClassification(pos, match[3].length, ClassificationType.jsxSelfClosingTagName); // element name + pos += match[3].length; + + const attrText = match[4]; + let attrPos = pos; + while (true) { + const attrMatch = attributeRegex.exec(attrText); + if (!attrMatch) { + break; + } + + const newAttrPos = pos + attrMatch.index; + if (newAttrPos > attrPos) { + pushCommentRange(attrPos, newAttrPos - attrPos); + attrPos = newAttrPos; + } + + pushClassification(attrPos, attrMatch[1].length, ClassificationType.jsxAttribute); // attribute name + attrPos += attrMatch[1].length; + + if (attrMatch[2].length) { + pushCommentRange(attrPos, attrMatch[2].length); // whitespace + attrPos += attrMatch[2].length; + } + + pushClassification(attrPos, attrMatch[3].length, ClassificationType.operator); // = + attrPos += attrMatch[3].length; + + if (attrMatch[4].length) { + pushCommentRange(attrPos, attrMatch[4].length); // whitespace + attrPos += attrMatch[4].length; + } + + pushClassification(attrPos, attrMatch[5].length, ClassificationType.jsxAttributeStringLiteralValue); // attribute value + attrPos += attrMatch[5].length; + } + + pos += match[4].length; + + if (pos > attrPos) { + pushCommentRange(attrPos, pos - attrPos); + } + + if (match[5]) { + pushClassification(pos, match[5].length, ClassificationType.punctuation); // /> + pos += match[5].length; + } + + const end = start + width; + if (pos < end) { + pushCommentRange(pos, end - pos); + } + + return true; + } + function processJSDocTemplateTag(tag: JSDocTemplateTag) { for (const child of tag.getChildren()) { processElement(child); diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash1.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash1.ts new file mode 100644 index 00000000000..13c27669ad5 --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash1.ts @@ -0,0 +1,15 @@ +/// + +//// /// + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/// "), + c.punctuation("<"), + c.jsxSelfClosingTagName("reference"), + c.comment(" "), + c.jsxAttribute("path"), + c.operator("="), + c.jsxAttributeStringLiteralValue("\"./module.ts\""), + c.comment(" "), + c.punctuation("/>")); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash10.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash10.ts new file mode 100644 index 00000000000..d1bb681b92e --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash10.ts @@ -0,0 +1,13 @@ +/// + +//// /// + +//// /// + +//// /// + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/// "), + c.punctuation("<"), + c.jsxSelfClosingTagName("reference"), + c.comment(" "), + c.jsxAttribute("path"), + c.operator("="), + c.jsxAttributeStringLiteralValue("\"./module.ts\""), + c.comment(" bad "), + c.jsxAttribute("types"), + c.operator("="), + c.jsxAttributeStringLiteralValue("\"node\""), + c.comment(" "), + c.punctuation("/>")); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash13.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash13.ts new file mode 100644 index 00000000000..182b01f7560 --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash13.ts @@ -0,0 +1,16 @@ +/// + +//// /// trailing + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/// "), + c.punctuation("<"), + c.jsxSelfClosingTagName("reference"), + c.comment(" "), + c.jsxAttribute("path"), + c.operator("="), + c.jsxAttributeStringLiteralValue("\"./module.ts\""), + c.comment(" "), + c.punctuation("/>"), + c.comment(" trailing")); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash14.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash14.ts new file mode 100644 index 00000000000..a7d16fea02c --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash14.ts @@ -0,0 +1,7 @@ +/// + +//// /// nonElement + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/// nonElement")); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash15.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash15.ts new file mode 100644 index 00000000000..8ec5ebbea5d --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash15.ts @@ -0,0 +1,25 @@ +/// + +//// /// +//// /// + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/// "), + c.punctuation("<"), + c.jsxSelfClosingTagName("reference"), + c.comment(" "), + c.jsxAttribute("path"), + c.operator("="), + c.jsxAttributeStringLiteralValue("\"./module1.ts\""), + c.comment(" "), + c.punctuation("/>"), + c.comment("/// "), + c.punctuation("<"), + c.jsxSelfClosingTagName("reference"), + c.comment(" "), + c.jsxAttribute("path"), + c.operator("="), + c.jsxAttributeStringLiteralValue("\"./module2.ts\""), + c.comment(" "), + c.punctuation("/>")); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash16.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash16.ts new file mode 100644 index 00000000000..1dbee22cb59 --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash16.ts @@ -0,0 +1,17 @@ +/// + +//// /// +//// 1 + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/// "), + c.punctuation("<"), + c.jsxSelfClosingTagName("reference"), + c.comment(" "), + c.jsxAttribute("path"), + c.operator("="), + c.jsxAttributeStringLiteralValue("\"./module.ts\""), + c.comment(" "), + c.punctuation("/>"), + c.numericLiteral("1")); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash2.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash2.ts new file mode 100644 index 00000000000..278d66dcb4a --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash2.ts @@ -0,0 +1,16 @@ +/// + +//// /// + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("///"), + c.punctuation("<"), + c.jsxSelfClosingTagName("reference"), + c.comment(" "), + c.jsxAttribute("path"), + c.comment(" "), + c.operator("="), + c.comment(" "), + c.jsxAttributeStringLiteralValue("\"./module.ts\""), + c.punctuation("/>")); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash3.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash3.ts new file mode 100644 index 00000000000..11d3b2462be --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash3.ts @@ -0,0 +1,19 @@ +/// + +//// /// + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/// "), + c.punctuation("<"), + c.jsxSelfClosingTagName("reference"), + c.comment(" "), + c.jsxAttribute("path"), + c.operator("="), + c.jsxAttributeStringLiteralValue("\"./module.ts\""), + c.comment(" "), + c.jsxAttribute("types"), + c.operator("="), + c.jsxAttributeStringLiteralValue("\"node\""), + c.comment(" "), + c.punctuation("/>")); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash4.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash4.ts new file mode 100644 index 00000000000..e656c811377 --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash4.ts @@ -0,0 +1,8 @@ +/// + +//// /// < + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/// "), + c.punctuation("<")); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash5.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash5.ts new file mode 100644 index 00000000000..c6f1de38f28 --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash5.ts @@ -0,0 +1,9 @@ +/// + +//// /// + +//// /// + +//// /// + +//// /// + +//// ///