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 @@
+///
+
+//// ///
+
+//// ///
+
+//// ///
+
+//// ///
+
+//// ///