Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6441,6 +6441,7 @@ namespace ts {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "auto" | "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeAutomaticOptionalChainCompletions?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
Expand Down
6 changes: 6 additions & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3007,6 +3007,12 @@ namespace ts.server.protocol {
* For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`.
*/
readonly includeCompletionsWithInsertText?: boolean;
/**
* Unless this option is `false`, or `includeCompletionsWithInsertText` is not enabled,
* member completion lists triggered with `.` will include entries on potentially-null and potentially-undefined
* values, with insertion text to replace preceding `.` tokens with `?.`.
*/
readonly includeAutomaticOptionalChainCompletions?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
Expand Down
29 changes: 24 additions & 5 deletions src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ namespace ts.Completions {
): CompletionEntry | undefined {
let insertText: string | undefined;
let replacementSpan: TextSpan | undefined;

const insertQuestionDot = origin && originIsNullableMember(origin);
const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess;
if (origin && originIsThisType(origin)) {
Expand Down Expand Up @@ -780,7 +781,7 @@ namespace ts.Completions {
sourceFile: SourceFile,
isUncheckedFile: boolean,
position: number,
preferences: Pick<UserPreferences, "includeCompletionsForModuleExports" | "includeCompletionsWithInsertText">,
preferences: Pick<UserPreferences, "includeCompletionsForModuleExports" | "includeCompletionsWithInsertText" | "includeAutomaticOptionalChainCompletions">,
detailsEntryId: CompletionEntryIdentifier | undefined,
host: LanguageServiceHost,
): CompletionData | Request | undefined {
Expand Down Expand Up @@ -1116,8 +1117,17 @@ namespace ts.Completions {
let type = typeChecker.getTypeOfSymbolAtLocation(symbol, node).getNonOptionalType();
let insertQuestionDot = false;
if (type.isNullableType()) {
insertQuestionDot = isRightOfDot && !isRightOfQuestionDot;
type = type.getNonNullableType();
const canCorrectToQuestionDot =
isRightOfDot &&
!isRightOfQuestionDot &&
preferences.includeAutomaticOptionalChainCompletions !== false;

if (canCorrectToQuestionDot || isRightOfQuestionDot) {
type = type.getNonNullableType();
if (canCorrectToQuestionDot) {
insertQuestionDot = true;
}
}
}
addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
}
Expand All @@ -1137,8 +1147,17 @@ namespace ts.Completions {
let type = typeChecker.getTypeAtLocation(node).getNonOptionalType();
let insertQuestionDot = false;
if (type.isNullableType()) {
insertQuestionDot = isRightOfDot && !isRightOfQuestionDot;
type = type.getNonNullableType();
const canCorrectToQuestionDot =
isRightOfDot &&
!isRightOfQuestionDot &&
preferences.includeAutomaticOptionalChainCompletions !== false;

if (canCorrectToQuestionDot || isRightOfQuestionDot) {
type = type.getNonNullableType();
if (canCorrectToQuestionDot) {
insertQuestionDot = true;
}
}
}
addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
}
Expand Down
7 changes: 7 additions & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3171,6 +3171,7 @@ declare namespace ts {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "auto" | "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeAutomaticOptionalChainCompletions?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
Expand Down Expand Up @@ -8295,6 +8296,12 @@ declare namespace ts.server.protocol {
* For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`.
*/
readonly includeCompletionsWithInsertText?: boolean;
/**
* Unless this option is `false`, or `includeCompletionsWithInsertText` is not enabled,
* member completion lists triggered with `.` will include entries on potentially-null and potentially-undefined
* values, with insertion text to replace preceding `.` tokens with `?.`.
*/
readonly includeAutomaticOptionalChainCompletions?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3171,6 +3171,7 @@ declare namespace ts {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "auto" | "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeAutomaticOptionalChainCompletions?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/// <reference path="fourslash.ts" />
// @strict: true

//// interface User {
//// address?: {
//// city: string;
//// "postal code": string;
//// }
//// };
//// declare const user: User;
//// user.address[|./**/|]

verify.completions({
marker: "",
exact: [],
preferences: {
includeInsertTextCompletions: true,
includeAutomaticOptionalChainCompletions: false
},
});
1 change: 1 addition & 0 deletions tests/cases/fourslash/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ declare namespace FourSlashInterface {
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeInsertTextCompletions?: boolean;
readonly includeAutomaticOptionalChainCompletions?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
}
Expand Down