diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts index 667cdf24ca8aa..61ec93800f07e 100644 --- a/src/services/stringCompletions.ts +++ b/src/services/stringCompletions.ts @@ -42,6 +42,7 @@ import { ensureTrailingDirectorySeparator, equateStringsCaseSensitive, escapeString, + every, Extension, fileExtensionIsOneOf, filter, @@ -133,6 +134,7 @@ import { singleElementArray, skipConstraint, skipParentheses, + some, SourceFile, startsWith, StringLiteralLike, @@ -141,6 +143,7 @@ import { supportedTSImplementationExtensions, Symbol, SyntaxKind, + TemplateLiteralType, textPart, TextSpan, tryAndIgnoreErrors, @@ -270,19 +273,29 @@ function convertStringLiteralCompletions( }; } case StringLiteralCompletionKind.Types: { + const textOfNode = getTextOfNode(contextToken); const quoteChar = contextToken.kind === SyntaxKind.NoSubstitutionTemplateLiteral ? CharacterCodes.backtick - : startsWith(getTextOfNode(contextToken), "'") + : textOfNode.charCodeAt(0) === CharacterCodes.singleQuote ? CharacterCodes.singleQuote : CharacterCodes.doubleQuote; - const entries = completion.types.map(type => ({ - name: escapeString(type.value, quoteChar), - kindModifiers: ScriptElementKindModifier.none, - kind: ScriptElementKind.string, - sortText: SortText.LocationPriority, - replacementSpan: getReplacementSpanForContextToken(contextToken, position), - commitCharacters: [], - })); + let tokenTextContent = textOfNode; + if (tokenTextContent) tokenTextContent = tokenTextContent.slice(1); + if (tokenTextContent.charCodeAt(tokenTextContent.length - 1) === quoteChar) tokenTextContent = tokenTextContent.slice(0, tokenTextContent.length - 1); + const uniques = new Map(); + const entries = mapDefined(completion.types, type => { + const name = type.isStringLiteral() + ? escapeString(type.value, quoteChar) + : getStringLiteralCompletionFromTemplateLiteralTypeAndTextOfNode(type, tokenTextContent, quoteChar); + return addToSeen(uniques, name) ? { + name, + kindModifiers: ScriptElementKindModifier.none, + kind: ScriptElementKind.string, + sortText: SortText.LocationPriority, + replacementSpan: getReplacementSpanForContextToken(contextToken, position), + commitCharacters: [], + } : undefined; + }); return { isGlobalCompletion: false, isMemberCompletion: false, @@ -297,6 +310,22 @@ function convertStringLiteralCompletions( } } +function getStringLiteralCompletionFromTemplateLiteralTypeAndTextOfNode(type: TemplateLiteralType, tokenTextContent: string, quoteChar: CharacterCodes.backtick | CharacterCodes.singleQuote | CharacterCodes.doubleQuote) { + let firstUnusedCharacterIndex = 0; + let textIndex = 0; + for (; textIndex < type.texts.length - 1; textIndex++) { + const escapedText = escapeString(type.texts[textIndex], quoteChar); + const match = tokenTextContent.indexOf(escapedText, firstUnusedCharacterIndex); + if (match < 0) { + // The current text was not matched so we suggest appending it or replacing if we aren't passed the first ${string}. + return textIndex === 0 ? escapedText : tokenTextContent + escapedText; + } + firstUnusedCharacterIndex = match + escapedText.length; + } + // We can always suggest appending the last text, since if it is already present, it can be part of the last ${string}. + return tokenTextContent + escapeString(type.texts[textIndex], quoteChar); +} + /** @internal */ export function getStringLiteralCompletionDetails(name: string, sourceFile: SourceFile, position: number, contextToken: Node | undefined, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken, preferences: UserPreferences) { if (!contextToken || !isStringLiteralLike(contextToken)) return undefined; @@ -315,7 +344,7 @@ function stringLiteralCompletionDetails(name: string, location: Node, completion return match && createCompletionDetailsForSymbol(match, match.name, checker, sourceFile, location, cancellationToken); } case StringLiteralCompletionKind.Types: - return find(completion.types, t => t.value === name) ? createCompletionDetails(name, ScriptElementKindModifier.none, ScriptElementKind.string, [textPart(name)]) : undefined; + return find(completion.types, t => t.isStringLiteral() && t.value === name) ? createCompletionDetails(name, ScriptElementKindModifier.none, ScriptElementKind.string, [textPart(name)]) : undefined; default: return Debug.assertNever(completion); } @@ -378,9 +407,10 @@ interface StringLiteralCompletionsFromProperties { readonly symbols: readonly Symbol[]; readonly hasIndexSignature: boolean; } +type StringLiteralLikeType = StringLiteralType | TemplateLiteralType; interface StringLiteralCompletionsFromTypes { readonly kind: StringLiteralCompletionKind.Types; - readonly types: readonly StringLiteralType[]; + readonly types: readonly (StringLiteralLikeType)[]; readonly isNewIdentifier: boolean; } type StringLiteralCompletion = { readonly kind: StringLiteralCompletionKind.Paths; readonly paths: readonly PathCompletion[]; } | StringLiteralCompletionsFromProperties | StringLiteralCompletionsFromTypes; @@ -411,7 +441,11 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL // }); return stringLiteralCompletionsForObjectLiteral(typeChecker, parent.parent); } - return fromContextualType() || fromContextualType(ContextFlags.None); + const fromContextualTypeWithCompletionFlag = fromContextualType(); + if (fromContextualTypeWithCompletionFlag && some(fromContextualTypeWithCompletionFlag.types, t => t.isStringLiteral())) { + return fromContextualTypeWithCompletionFlag; + } + return fromContextualType(ContextFlags.None); case SyntaxKind.ElementAccessExpression: { const { expression, argumentExpression } = parent as ElementAccessExpression; @@ -456,7 +490,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL if (!contextualTypes) { return; } - const literals = contextualTypes.types.filter(literal => !tracker.hasValue(literal.value)); + const literals = contextualTypes.types.filter(literal => literal.isStringLiteral() && !tracker.hasValue(literal.value)); return { kind: StringLiteralCompletionKind.Types, types: literals, isNewIdentifier: false }; case SyntaxKind.ImportSpecifier: @@ -476,8 +510,13 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL const uniques = exports.filter(e => e.escapedName !== InternalSymbolName.Default && !existing.has(e.escapedName)); return { kind: StringLiteralCompletionKind.Properties, symbols: uniques, hasIndexSignature: false }; - default: - return fromContextualType() || fromContextualType(ContextFlags.None); + default: { + const fromContextualTypeWithCompletionFlag = fromContextualType(); + if (fromContextualTypeWithCompletionFlag && some(fromContextualTypeWithCompletionFlag.types, t => t.isStringLiteral())) { + return fromContextualTypeWithCompletionFlag; + } + return fromContextualType(ContextFlags.None); + } } function fromUnionableLiteralType(grandParent: Node): StringLiteralCompletionsFromTypes | StringLiteralCompletionsFromProperties | undefined { @@ -486,7 +525,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL case SyntaxKind.TypeReference: { const typeArgument = findAncestor(parent, n => n.parent === grandParent) as LiteralTypeNode; if (typeArgument) { - return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(typeChecker.getTypeArgumentConstraint(typeArgument)), isNewIdentifier: false }; + return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralLikeTypes(typeChecker.getTypeArgumentConstraint(typeArgument)), isNewIdentifier: false }; } return undefined; } @@ -511,7 +550,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL if (result.kind === StringLiteralCompletionKind.Properties) { return { kind: StringLiteralCompletionKind.Properties, symbols: result.symbols.filter(sym => !contains(alreadyUsedTypes, sym.name)), hasIndexSignature: result.hasIndexSignature }; } - return { kind: StringLiteralCompletionKind.Types, types: result.types.filter(t => !contains(alreadyUsedTypes, t.value)), isNewIdentifier: false }; + return { kind: StringLiteralCompletionKind.Types, types: result.types.filter(t => t.isStringLiteral() && !contains(alreadyUsedTypes, t.value)), isNewIdentifier: false }; } default: return undefined; @@ -521,7 +560,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL function fromContextualType(contextFlags: ContextFlags = ContextFlags.Completions): StringLiteralCompletionsFromTypes | undefined { // Get completion for string literal from string literal type // i.e. var x: "hi" | "hello" = "/*completion position*/" - const types = getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker, contextFlags)); + const types = getStringLiteralLikeTypes(getContextualTypeFromParent(node, typeChecker, contextFlags)); if (!types.length) { return; } @@ -559,7 +598,7 @@ function getStringLiteralCompletionsFromSignature(call: CallLikeExpression, arg: } } isNewIdentifier = isNewIdentifier || !!(type.flags & TypeFlags.String); - return getStringLiteralTypes(type, uniques); + return getStringLiteralLikeTypes(type, uniques); }); return length(types) ? { kind: StringLiteralCompletionKind.Types, types, isNewIdentifier } : undefined; } @@ -591,11 +630,12 @@ function stringLiteralCompletionsForObjectLiteral(checker: TypeChecker, objectLi }; } -function getStringLiteralTypes(type: Type | undefined, uniques = new Map()): readonly StringLiteralType[] { +function getStringLiteralLikeTypes(type: Type | undefined, uniques = new Map()): readonly StringLiteralLikeType[] { if (!type) return emptyArray; type = skipConstraint(type); - return type.isUnion() ? flatMap(type.types, t => getStringLiteralTypes(t, uniques)) : - type.isStringLiteral() && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, type.value) ? [type] : emptyArray; + return type.isUnion() ? flatMap(type.types, t => getStringLiteralLikeTypes(t, uniques)) : + type.isStringLiteral() && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, type.value) + || (type.flags & TypeFlags.TemplateLiteral && every((type as TemplateLiteralType).types, t => !!(TypeFlags.String & t.flags))) ? [type as StringLiteralLikeType] : emptyArray; } interface NameAndKind { diff --git a/tests/baselines/reference/completionsTemplateLiterals.baseline b/tests/baselines/reference/completionsTemplateLiterals.baseline new file mode 100644 index 0000000000000..2ad9684b6a0d8 --- /dev/null +++ b/tests/baselines/reference/completionsTemplateLiterals.baseline @@ -0,0 +1,1064 @@ +// === Completions === +=== /tests/cases/fourslash/file1.ts === +// declare let x: `${"prefix1"|"prefix2"}${string}${"middle1"|"middle2"}${string}${"suffix1"|"suffix2"}`; +// x = " +// ^ +// | ---------------------------------------------------------------------- +// | (string) prefix1 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "" +// ^ +// | ---------------------------------------------------------------------- +// | (string) prefix1 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "wrong +// ^^^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "wrong" +// ^^^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "pre +// ^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "pre" +// ^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "prefix1_ +// ^^^^^^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1_middle1 +// | (string) prefix1_middle2 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "prefix1_" +// ^^^^^^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1_middle1 +// | (string) prefix1_middle2 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "prefix1_middle1_ +// ^^^^^^^^^^^^^^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1_middle1_suffix1 +// | (string) prefix1_middle1_suffix2 +// | (string) prefix1_middle1_middle2 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "prefix1_middle1_" +// ^^^^^^^^^^^^^^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1_middle1_suffix1 +// | (string) prefix1_middle1_suffix2 +// | (string) prefix1_middle1_middle2 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "prefix1_middle1_suffix1 +// ^^^^^^^^^^^^^^^^^^^^^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1_middle1_suffix1suffix1 +// | (string) prefix1_middle1_suffix1suffix2 +// | (string) prefix1_middle1_suffix1middle2 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// x = "prefix1_middle1_suffix1" +// ^^^^^^^^^^^^^^^^^^^^^^^ +// | ---------------------------------------------------------------------- +// | (string) prefix1_middle1_suffix1suffix1 +// | (string) prefix1_middle1_suffix1suffix2 +// | (string) prefix1_middle1_suffix1middle2 +// | (string) prefix2 +// | ---------------------------------------------------------------------- +// +// declare let y: `"'${string}'"`; +// y = " +// ^ +// | ---------------------------------------------------------------------- +// | (string) \"' +// | ---------------------------------------------------------------------- +// y = "" +// ^ +// | ---------------------------------------------------------------------- +// | (string) \"' +// | ---------------------------------------------------------------------- +// y = ' +// ^ +// | ---------------------------------------------------------------------- +// | (string) "\' +// | ---------------------------------------------------------------------- +// y = '' +// ^ +// | ---------------------------------------------------------------------- +// | (string) "\' +// | ---------------------------------------------------------------------- +// y = '"\'\\\"\\ +// ^^^^^^^^^ +// | ---------------------------------------------------------------------- +// | (string) "\'\\\"\\\'" +// | ---------------------------------------------------------------------- +// +// declare let z: `${string}suffix`; +// z = " +// ^ +// | ---------------------------------------------------------------------- +// | (string) suffix +// | ---------------------------------------------------------------------- +// z = "" +// ^ +// | ---------------------------------------------------------------------- +// | (string) suffix +// | ---------------------------------------------------------------------- +// z = ' +// ^ +// | ---------------------------------------------------------------------- +// | (string) suffix +// | ---------------------------------------------------------------------- +// z = '' +// ^ +// | ---------------------------------------------------------------------- +// | (string) suffix +// | ---------------------------------------------------------------------- +// +// declare let w: `prefix${number}suffix`; +// w = "prefix +// ^ +// | ---------------------------------------------------------------------- +// | No completions at /*12*/. +// | ---------------------------------------------------------------------- + +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 108, + "name": "1a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "entries": [ + { + "name": "prefix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 114, + "name": "1b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 114, + "length": 0 + }, + "entries": [ + { + "name": "prefix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 114, + "length": 0 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 114, + "length": 0 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 126, + "name": "2a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 121, + "length": 5 + }, + "entries": [ + { + "name": "prefix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 121, + "length": 5 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 121, + "length": 5 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 137, + "name": "2b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 132, + "length": 5 + }, + "entries": [ + { + "name": "prefix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 132, + "length": 5 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 132, + "length": 5 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 147, + "name": "3a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 144, + "length": 3 + }, + "entries": [ + { + "name": "prefix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 144, + "length": 3 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 144, + "length": 3 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 156, + "name": "3b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 153, + "length": 3 + }, + "entries": [ + { + "name": "prefix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 153, + "length": 3 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 153, + "length": 3 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 171, + "name": "4a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 163, + "length": 8 + }, + "entries": [ + { + "name": "prefix1_middle1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 163, + "length": 8 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 163, + "length": 8 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 163, + "length": 8 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 185, + "name": "4b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 177, + "length": 8 + }, + "entries": [ + { + "name": "prefix1_middle1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 177, + "length": 8 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 177, + "length": 8 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 177, + "length": 8 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 208, + "name": "5a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 192, + "length": 16 + }, + "entries": [ + { + "name": "prefix1_middle1_suffix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 192, + "length": 16 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle1_suffix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 192, + "length": 16 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle1_middle2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 192, + "length": 16 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 192, + "length": 16 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 230, + "name": "5b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 214, + "length": 16 + }, + "entries": [ + { + "name": "prefix1_middle1_suffix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 214, + "length": 16 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle1_suffix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 214, + "length": 16 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle1_middle2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 214, + "length": 16 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 214, + "length": 16 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 260, + "name": "6a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 237, + "length": 23 + }, + "entries": [ + { + "name": "prefix1_middle1_suffix1suffix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 237, + "length": 23 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle1_suffix1suffix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 237, + "length": 23 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle1_suffix1middle2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 237, + "length": 23 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 237, + "length": 23 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 289, + "name": "6b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 266, + "length": 23 + }, + "entries": [ + { + "name": "prefix1_middle1_suffix1suffix1", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 266, + "length": 23 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle1_suffix1suffix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 266, + "length": 23 + }, + "commitCharacters": [] + }, + { + "name": "prefix1_middle1_suffix1middle2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 266, + "length": 23 + }, + "commitCharacters": [] + }, + { + "name": "prefix2", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 266, + "length": 23 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 329, + "name": "7a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "entries": [ + { + "name": "\\\"'", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 335, + "name": "7b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 335, + "length": 0 + }, + "entries": [ + { + "name": "\\\"'", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 335, + "length": 0 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 342, + "name": "8a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "entries": [ + { + "name": "\"\\'", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 348, + "name": "8b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 348, + "length": 0 + }, + "entries": [ + { + "name": "\"\\'", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 348, + "length": 0 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 364, + "name": "9" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 355, + "length": 9 + }, + "entries": [ + { + "name": "\"\\'\\\\\\\"\\\\\\'\"", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 355, + "length": 9 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 405, + "name": "10a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "entries": [ + { + "name": "suffix", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 411, + "name": "10b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 411, + "length": 0 + }, + "entries": [ + { + "name": "suffix", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 411, + "length": 0 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 418, + "name": "11a" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "entries": [ + { + "name": "suffix", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 424, + "name": "11b" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 424, + "length": 0 + }, + "entries": [ + { + "name": "suffix", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 424, + "length": 0 + }, + "commitCharacters": [] + } + ], + "defaultCommitCharacters": [ + ".", + ",", + ";" + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/file1.ts", + "position": 478, + "name": "12" + }, + "item": {} + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/completionsTemplateLiterals.ts b/tests/cases/fourslash/completionsTemplateLiterals.ts new file mode 100644 index 0000000000000..9306b04f39180 --- /dev/null +++ b/tests/cases/fourslash/completionsTemplateLiterals.ts @@ -0,0 +1,34 @@ +/// + +// @Filename: file1.ts +////declare let x: `${"prefix1"|"prefix2"}${string}${"middle1"|"middle2"}${string}${"suffix1"|"suffix2"}`; +////x = "/*1a*/ +////x = "/*1b*/" +////x = "wrong/*2a*/ +////x = "wrong/*2b*/" +////x = "pre/*3a*/ +////x = "pre/*3b*/" +////x = "prefix1_/*4a*/ +////x = "prefix1_/*4b*/" +////x = "prefix1_middle1_/*5a*/ +////x = "prefix1_middle1_/*5b*/" +////x = "prefix1_middle1_suffix1/*6a*/ +////x = "prefix1_middle1_suffix1/*6b*/" +//// +////declare let y: `"'${string}'"`; +////y = "/*7a*/ +////y = "/*7b*/" +////y = '/*8a*/ +////y = '/*8b*/' +////y = '"\'\\\"\\/*9*/ +//// +////declare let z: `${string}suffix`; +////z = "/*10a*/ +////z = "/*10b*/" +////z = '/*11a*/ +////z = '/*11b*/' +//// +////declare let w: `prefix${number}suffix`; +////w = "prefix/*12*/ + +verify.baselineCompletions()