diff --git a/src/services/services.ts b/src/services/services.ts index 8d233811c8151..8b20cdbe8ed3b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2498,25 +2498,48 @@ module ts { } function isInStringOrRegularExpressionLiteral(previousToken: Node): boolean { - if (previousToken.kind === SyntaxKind.StringLiteral) { + if (previousToken.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(previousToken.kind)) { // The position has to be either: 1. entirely within the token text, or // 2. at the end position, and the string literal is not terminated + var start = previousToken.getStart(); var end = previousToken.getEnd(); + if (start < position && position < end) { return true; } else if (position === end) { var width = end - start; var text = previousToken.getSourceFile().text; - return width <= 1 || - text.charCodeAt(start) !== text.charCodeAt(end - 1) || - text.charCodeAt(end - 2) === CharacterCodes.backslash; + + // If the token is a single character, or its second-to-last charcter indicates an escape code, + // then we can immediately say that we are in the middle of an unclosed string. + if (width <= 1 || text.charCodeAt(end - 2) === CharacterCodes.backslash) { + return true; + } + + // Now check if the last character is a closing character for the token. + switch (previousToken.kind) { + case SyntaxKind.StringLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return text.charCodeAt(start) !== text.charCodeAt(end - 1); + + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + return text.charCodeAt(end - 1) !== CharacterCodes.openBrace + || text.charCodeAt(end - 2) !== CharacterCodes.$; + + case SyntaxKind.TemplateTail: + return text.charCodeAt(end - 1) !== CharacterCodes.backtick; + } + + return false; } } else if (previousToken.kind === SyntaxKind.RegularExpressionLiteral) { return previousToken.getStart() < position && position < previousToken.getEnd(); } + return false; } diff --git a/tests/cases/fourslash/completionListInTemplateLiteralParts1.ts b/tests/cases/fourslash/completionListInTemplateLiteralParts1.ts new file mode 100644 index 0000000000000..a71584bdbdd2f --- /dev/null +++ b/tests/cases/fourslash/completionListInTemplateLiteralParts1.ts @@ -0,0 +1,11 @@ +/// + +/////*0*/` $ { ${/*1*/ 10/*2*/ + 1.1/*3*/ /*4*/} 12312`/*5*/ +//// +/////*6*/`asdasd${/*7*/ 2 + 1.1 /*8*/} 12312 { + +test.markers().forEach(marker => { + goTo.position(marker.position); + + verify.completionListItemsCountIsGreaterThan(0) +}} \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInTemplateLiteralPartsNegatives1.ts b/tests/cases/fourslash/completionListInTemplateLiteralPartsNegatives1.ts new file mode 100644 index 0000000000000..1624192057b3f --- /dev/null +++ b/tests/cases/fourslash/completionListInTemplateLiteralPartsNegatives1.ts @@ -0,0 +1,11 @@ +/// + +////`/*0*/ /*1*/$ /*2*/{ /*3*/$/*4*/{ 10 + 1.1 }/*5*/ 12312/*6*/` +//// +////`asdasd$/*7*/{ 2 + 1.1 }/*8*/ 12312 /*9*/{/*10*/ + +test.markers().forEach(marker => { + goTo.position(marker.position); + + verify.completionListIsEmpty() +} \ No newline at end of file