diff --git a/src/services/services.ts b/src/services/services.ts
index 8ff61b05a7a..68177066b86 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -5549,15 +5549,24 @@ module ts {
addResult(end - start, classFromKind(token));
if (end >= text.length) {
- // We're at the end.
if (token === SyntaxKind.StringLiteral) {
// Check to see if we finished up on a multiline string literal.
var tokenText = scanner.getTokenText();
if (scanner.isUnterminated()) {
- var quoteChar = tokenText.charCodeAt(0);
- result.finalLexState = quoteChar === CharacterCodes.doubleQuote
- ? EndOfLineState.InDoubleQuoteStringLiteral
- : EndOfLineState.InSingleQuoteStringLiteral;
+ var lastCharIndex = tokenText.length - 1;
+
+ var numBackslashes = 0;
+ while (tokenText.charCodeAt(lastCharIndex - numBackslashes) === CharacterCodes.backslash) {
+ numBackslashes++;
+ }
+
+ // If we have an odd number of backslashes, then the multiline string is unclosed
+ if (numBackslashes & 1) {
+ var quoteChar = tokenText.charCodeAt(0);
+ result.finalLexState = quoteChar === CharacterCodes.doubleQuote
+ ? EndOfLineState.InDoubleQuoteStringLiteral
+ : EndOfLineState.InSingleQuoteStringLiteral;
+ }
}
}
else if (token === SyntaxKind.MultiLineCommentTrivia) {
diff --git a/tests/cases/fourslash/completionListInStringLiterals1.ts b/tests/cases/fourslash/completionListInStringLiterals1.ts
new file mode 100644
index 00000000000..e394e8dfe7c
--- /dev/null
+++ b/tests/cases/fourslash/completionListInStringLiterals1.ts
@@ -0,0 +1,10 @@
+///
+
+////"/*1*/ /*2*/\/*3*/
+//// /*4*/ \\/*5*/
+
+test.markers().forEach(marker => {
+ goTo.position(marker.position);
+
+ verify.completionListIsEmpty()
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/completionListInStringLiterals2.ts b/tests/cases/fourslash/completionListInStringLiterals2.ts
new file mode 100644
index 00000000000..10cb05a4f91
--- /dev/null
+++ b/tests/cases/fourslash/completionListInStringLiterals2.ts
@@ -0,0 +1,11 @@
+///
+
+////"/*1*/ /*2*/\/*3*/
+//// /*4*/ \\\/*5*/
+//// /*6*/
+
+test.markers().forEach(marker => {
+ goTo.position(marker.position);
+
+ verify.completionListIsEmpty()
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/signatureHelpTaggedTemplatesIncomplete7.ts b/tests/cases/fourslash/signatureHelpTaggedTemplatesIncomplete7.ts
new file mode 100644
index 00000000000..ae322acf0b8
--- /dev/null
+++ b/tests/cases/fourslash/signatureHelpTaggedTemplatesIncomplete7.ts
@@ -0,0 +1,21 @@
+///
+
+//// function f(templateStrings, x, y, z) { return 10; }
+//// function g(templateStrings, x, y, z) { return ""; }
+////
+//// f ` ${ 123 } /*1*/${ } /*2*/\/*3*/
+//// /*4*/\\/*5*/
+//// /*6*/\\\/*7*/
+//// /*8*/
+
+test.markers().forEach(m => {
+ goTo.position(m.position);
+
+ verify.signatureHelpCountIs(1);
+ verify.signatureHelpArgumentCountIs(1);
+
+ verify.currentSignatureParameterCountIs(4);
+ verify.currentSignatureHelpIs('f(templateStrings: any, x: any, y: any, z: any): number');
+ verify.currentParameterHelpArgumentNameIs("templateStrings");
+ verify.currentParameterSpanIs("templateStrings: any");
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/signatureHelpTaggedTemplatesIncomplete8.ts b/tests/cases/fourslash/signatureHelpTaggedTemplatesIncomplete8.ts
new file mode 100644
index 00000000000..86455a65ccd
--- /dev/null
+++ b/tests/cases/fourslash/signatureHelpTaggedTemplatesIncomplete8.ts
@@ -0,0 +1,18 @@
+///
+
+//// function f(templateStrings, x, y, z) { return 10; }
+//// function g(templateStrings, x, y, z) { return ""; }
+////
+//// f `/*1*/\/*2*/`/*3*/ /*4*/
+
+test.markers().forEach(m => {
+ goTo.position(m.position);
+
+ verify.signatureHelpCountIs(1);
+ verify.signatureHelpArgumentCountIs(1);
+
+ verify.currentSignatureParameterCountIs(4);
+ verify.currentSignatureHelpIs('f(templateStrings: any, x: any, y: any, z: any): number');
+ verify.currentParameterHelpArgumentNameIs("templateStrings");
+ verify.currentParameterSpanIs("templateStrings: any");
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/signatureHelpTaggedTemplatesIncomplete9.ts b/tests/cases/fourslash/signatureHelpTaggedTemplatesIncomplete9.ts
new file mode 100644
index 00000000000..de42a33b397
--- /dev/null
+++ b/tests/cases/fourslash/signatureHelpTaggedTemplatesIncomplete9.ts
@@ -0,0 +1,18 @@
+///
+
+//// function f(templateStrings, x, y, z) { return 10; }
+//// function g(templateStrings, x, y, z) { return ""; }
+////
+//// f `/*1*/ \\\/*2*/`/*3*/ /*4*/
+
+test.markers().forEach(m => {
+ goTo.position(m.position);
+
+ verify.signatureHelpCountIs(1);
+ verify.signatureHelpArgumentCountIs(1);
+
+ verify.currentSignatureParameterCountIs(4);
+ verify.currentSignatureHelpIs('f(templateStrings: any, x: any, y: any, z: any): number');
+ verify.currentParameterHelpArgumentNameIs("templateStrings");
+ verify.currentParameterSpanIs("templateStrings: any");
+});
\ No newline at end of file
diff --git a/tests/cases/unittests/services/colorization.ts b/tests/cases/unittests/services/colorization.ts
index 3031cefb56c..b29f119f944 100644
--- a/tests/cases/unittests/services/colorization.ts
+++ b/tests/cases/unittests/services/colorization.ts
@@ -130,20 +130,76 @@ describe('Colorization', function () {
operator(","));
});
- it("correctly classifies an unterminated multi-line string", function () {
+ it("correctly classifies a multi-line string with one backslash", function () {
test("'line1\\",
ts.EndOfLineState.Start,
stringLiteral("'line1\\"),
finalEndOfLineState(ts.EndOfLineState.InSingleQuoteStringLiteral));
});
- it("correctly classifies the second line of an unterminated multi-line string", function () {
+ it("correctly classifies a multi-line string with three backslashes", function () {
+ test("'line1\\\\\\",
+ ts.EndOfLineState.Start,
+ stringLiteral("'line1\\\\\\"),
+ finalEndOfLineState(ts.EndOfLineState.InSingleQuoteStringLiteral));
+ });
+
+ it("correctly classifies an unterminated single-line string with no backslashes", function () {
+ test("'line1",
+ ts.EndOfLineState.Start,
+ stringLiteral("'line1"),
+ finalEndOfLineState(ts.EndOfLineState.Start));
+ });
+
+ it("correctly classifies an unterminated single-line string with two backslashes", function () {
+ test("'line1\\\\",
+ ts.EndOfLineState.Start,
+ stringLiteral("'line1\\\\"),
+ finalEndOfLineState(ts.EndOfLineState.Start));
+ });
+
+ it("correctly classifies an unterminated single-line string with four backslashes", function () {
+ test("'line1\\\\\\\\",
+ ts.EndOfLineState.Start,
+ stringLiteral("'line1\\\\\\\\"),
+ finalEndOfLineState(ts.EndOfLineState.Start));
+ });
+
+ it("correctly classifies the continuing line of a multi-line string ending in one backslash", function () {
test("\\",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral("\\"),
finalEndOfLineState(ts.EndOfLineState.InDoubleQuoteStringLiteral));
});
+ it("correctly classifies the continuing line of a multi-line string ending in three backslashes", function () {
+ test("\\",
+ ts.EndOfLineState.InDoubleQuoteStringLiteral,
+ stringLiteral("\\"),
+ finalEndOfLineState(ts.EndOfLineState.InDoubleQuoteStringLiteral));
+ });
+
+ it("correctly classifies the last line of an unterminated multi-line string ending in no backslashes", function () {
+ test(" ",
+ ts.EndOfLineState.InDoubleQuoteStringLiteral,
+ stringLiteral(" "),
+ finalEndOfLineState(ts.EndOfLineState.Start));
+ });
+
+ it("correctly classifies the last line of an unterminated multi-line string ending in two backslashes", function () {
+ test("\\\\",
+ ts.EndOfLineState.InDoubleQuoteStringLiteral,
+ stringLiteral("\\\\"),
+ finalEndOfLineState(ts.EndOfLineState.Start));
+ });
+
+ it("correctly classifies the last line of an unterminated multi-line string ending in four backslashes", function () {
+ test("\\\\\\\\",
+ ts.EndOfLineState.InDoubleQuoteStringLiteral,
+ stringLiteral("\\\\\\\\"),
+ finalEndOfLineState(ts.EndOfLineState.Start));
+ });
+
it("correctly classifies the last line of a multi-line string", function () {
test("'",
ts.EndOfLineState.InSingleQuoteStringLiteral,