mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-04-22 04:14:47 -05:00
Exhaustive case completion for switch statements (#50996)
* fix services' type's isLiteral * update literal completions tests * initial prototype * use symbol to expression. TODO: filter existing, replace import nodes * WIP * WIP * remove booleans from literals * trigger at case keyword positions * clean up tests * fix element access expression case * refactor dealing with existing values into a tracker * fix merge errors * cleanup and more tests * fix lint errors * more merge conflict fixes and cleanup * use appropriate quotes * small indentation fix * refactor case clause tracker * experiment: support tabstops after each case clause * address small CR comments * fix completion entry details; add test case * fix lint errors * remove space before tab stops; refactor
This commit is contained in:
committed by
GitHub
parent
5435efbf37
commit
6a3c9ea125
107
tests/cases/fourslash/exhaustiveCaseCompletions1.ts
Normal file
107
tests/cases/fourslash/exhaustiveCaseCompletions1.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// Basic tests
|
||||
|
||||
// @newline: LF
|
||||
//// enum E {
|
||||
//// A = 0,
|
||||
//// B = "B",
|
||||
//// C = "C",
|
||||
//// }
|
||||
//// // Mixed union
|
||||
//// declare const u: E.A | E.B | 1;
|
||||
//// switch (u) {
|
||||
//// case/*1*/
|
||||
//// }
|
||||
//// // Union enum
|
||||
//// declare const e: E;
|
||||
//// switch (e) {
|
||||
//// case/*2*/
|
||||
//// }
|
||||
//// enum F {
|
||||
//// D = 1 << 0,
|
||||
//// E = 1 << 1,
|
||||
//// F = 1 << 2,
|
||||
//// }
|
||||
////
|
||||
//// declare const f: F;
|
||||
//// switch (f) {
|
||||
//// case/*3*/
|
||||
//// }
|
||||
|
||||
verify.completions(
|
||||
{
|
||||
marker: "1",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case E.A: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case E.A:
|
||||
case E.B:
|
||||
case 1:`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "2",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case E.A: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case E.A:
|
||||
case E.B:
|
||||
case E.C:`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "3",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case F.D: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case F.D:
|
||||
case F.E:
|
||||
case F.F:`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "3",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case F.D: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
isSnippet: true,
|
||||
insertText:
|
||||
`case F.D:$1
|
||||
case F.E:$2
|
||||
case F.F:$3`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
includeCompletionsWithSnippetText: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
77
tests/cases/fourslash/exhaustiveCaseCompletions2.ts
Normal file
77
tests/cases/fourslash/exhaustiveCaseCompletions2.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// Import-related cases
|
||||
|
||||
// @newline: LF
|
||||
// @Filename: /dep.ts
|
||||
//// export enum E {
|
||||
//// A = 0,
|
||||
//// B = "B",
|
||||
//// C = "C",
|
||||
//// }
|
||||
//// declare const u: E.A | E.B | 1;
|
||||
//// export { u };
|
||||
|
||||
// @Filename: /main.ts
|
||||
//// import { u } from "./dep";
|
||||
//// switch (u) {
|
||||
//// case/*1*/
|
||||
//// }
|
||||
|
||||
// @Filename: /other.ts
|
||||
//// import * as d from "./dep";
|
||||
//// declare const u: d.E;
|
||||
//// switch (u) {
|
||||
//// case/*2*/
|
||||
//// }
|
||||
|
||||
verify.completions(
|
||||
{
|
||||
marker: "1",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case E.A: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case E.A:
|
||||
case E.B:
|
||||
case 1:`,
|
||||
hasAction: true,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "2",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case d.E.A: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case d.E.A:
|
||||
case d.E.B:
|
||||
case d.E.C:`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
verify.applyCodeActionFromCompletion("1", {
|
||||
name: "case E.A: ...",
|
||||
source: "SwitchCases/",
|
||||
description: "Includes imports of types referenced by 'case E.A: ...'",
|
||||
newFileContent:
|
||||
`import { E, u } from "./dep";
|
||||
switch (u) {
|
||||
case
|
||||
}`,
|
||||
});
|
||||
112
tests/cases/fourslash/exhaustiveCaseCompletions3.ts
Normal file
112
tests/cases/fourslash/exhaustiveCaseCompletions3.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// Where the exhaustive case completion appears or not.
|
||||
|
||||
// @newline: LF
|
||||
// @Filename: /main.ts
|
||||
//// enum E {
|
||||
//// A = 0,
|
||||
//// B = "B",
|
||||
//// C = "C",
|
||||
//// }
|
||||
//// declare const u: E;
|
||||
//// switch (u) {
|
||||
//// case/*1*/
|
||||
//// }
|
||||
//// switch (u) {
|
||||
//// /*2*/
|
||||
//// }
|
||||
//// switch (u) {
|
||||
//// case 1:
|
||||
//// /*3*/
|
||||
//// }
|
||||
//// switch (u) {
|
||||
//// c/*4*/
|
||||
//// }
|
||||
//// switch (u) {
|
||||
//// case /*5*/
|
||||
//// }
|
||||
//// /*6*/
|
||||
//// switch (u) {
|
||||
//// /*7*/
|
||||
////
|
||||
|
||||
const exhaustiveCaseCompletion = {
|
||||
name: "case E.A: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case E.A:
|
||||
case E.B:
|
||||
case E.C:`,
|
||||
};
|
||||
|
||||
verify.completions(
|
||||
{
|
||||
marker: "1",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
exhaustiveCaseCompletion,
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "2",
|
||||
includes: [
|
||||
exhaustiveCaseCompletion,
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
marker: "3",
|
||||
includes: [
|
||||
exhaustiveCaseCompletion,
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
marker: "4",
|
||||
includes: [
|
||||
exhaustiveCaseCompletion,
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
marker: "5",
|
||||
includes: [
|
||||
exhaustiveCaseCompletion,
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
marker: "6",
|
||||
exact: [
|
||||
"E",
|
||||
"u",
|
||||
...completion.globals,
|
||||
exhaustiveCaseCompletion,
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
marker: "7",
|
||||
includes: [
|
||||
exhaustiveCaseCompletion,
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
179
tests/cases/fourslash/exhaustiveCaseCompletions4.ts
Normal file
179
tests/cases/fourslash/exhaustiveCaseCompletions4.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// Filter existing values.
|
||||
|
||||
// @newline: LF
|
||||
//// enum E {
|
||||
//// A = 0,
|
||||
//// B = "B",
|
||||
//// C = "C",
|
||||
//// }
|
||||
//// // Filtering existing literals
|
||||
//// declare const u: E.A | E.B | 1 | 1n | "1";
|
||||
//// switch (u) {
|
||||
//// case E.A:
|
||||
//// case 1:
|
||||
//// case 1n:
|
||||
//// case 0x1n:
|
||||
//// case "1":
|
||||
//// case `1`:
|
||||
//// case `1${u}`:
|
||||
//// case/*1*/
|
||||
//// }
|
||||
//// declare const v: E.A | "1" | "2";
|
||||
//// switch (v) {
|
||||
//// case 0:
|
||||
//// case `1`:
|
||||
//// /*2*/
|
||||
//// }
|
||||
//// // Filtering repreated enum members
|
||||
//// enum F {
|
||||
//// A = "A",
|
||||
//// B = "B",
|
||||
//// C = A,
|
||||
//// }
|
||||
//// declare const x: F;
|
||||
//// switch (x) {
|
||||
//// /*3*/
|
||||
//// }
|
||||
//// // Enum with computed elements
|
||||
//// enum G {
|
||||
//// C = 0,
|
||||
//// D = 1 << 1,
|
||||
//// E = 1 << 2,
|
||||
//// OtherD = D,
|
||||
//// DorE = D | E,
|
||||
//// }
|
||||
//// declare const y: G;
|
||||
//// switch (y) {
|
||||
//// /*4*/
|
||||
//// }
|
||||
//// switch (y) {
|
||||
//// case 0: // same as G.C
|
||||
//// case 1: // same as G.D, but we don't know it
|
||||
//// case 3: // same as G.DorE, but we don't know
|
||||
//// /*5*/
|
||||
//// }
|
||||
////
|
||||
//// // Already exhaustive switch
|
||||
//// enum H {
|
||||
//// A = "A",
|
||||
//// B = "B",
|
||||
//// C = "C",
|
||||
//// }
|
||||
//// declare const z: H;
|
||||
//// switch (z) {
|
||||
//// case H.A:
|
||||
//// case H.B:
|
||||
//// case H.C:
|
||||
//// /*6*/
|
||||
//// }
|
||||
|
||||
verify.completions(
|
||||
{
|
||||
marker: "1",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case E.B: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case E.B:`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "2",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: `case "2": ...`,
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case "2":`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "3",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case F.A: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case F.A:
|
||||
case F.B:`, // no C because C's value is the same as A's
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "4",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case G.C: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case G.C:
|
||||
case G.D:
|
||||
case G.E:
|
||||
case G.DorE:`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "5",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: "case G.D: ...",
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case G.D:
|
||||
case G.E:
|
||||
case G.DorE:`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
marker: "6",
|
||||
isNewIdentifierLocation: false,
|
||||
// No exhaustive case completion offered here because the switch is already exhaustive
|
||||
exact: [
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
"u",
|
||||
"v",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
...completion.globals,
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
35
tests/cases/fourslash/exhaustiveCaseCompletions5.ts
Normal file
35
tests/cases/fourslash/exhaustiveCaseCompletions5.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// Filter existing values.
|
||||
|
||||
// @newline: LF
|
||||
//// enum P {
|
||||
//// " Space",
|
||||
//// Bar,
|
||||
//// }
|
||||
////
|
||||
//// declare const p: P;
|
||||
////
|
||||
//// switch (p) {
|
||||
//// /*1*/
|
||||
//// }
|
||||
|
||||
verify.completions(
|
||||
{
|
||||
marker: "1",
|
||||
isNewIdentifierLocation: false,
|
||||
includes: [
|
||||
{
|
||||
name: `case P[" Space"]: ...`,
|
||||
source: completion.CompletionSource.SwitchCases,
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
insertText:
|
||||
`case P[" Space"]:
|
||||
case P.Bar:`,
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -884,6 +884,7 @@ declare namespace completion {
|
||||
ClassMemberSnippet = "ClassMemberSnippet/",
|
||||
TypeOnlyAlias = "TypeOnlyAlias/",
|
||||
ObjectLiteralMethodSnippet = "ObjectLiteralMethodSnippet/",
|
||||
SwitchCases = "SwitchCases/",
|
||||
}
|
||||
export const globalThisEntry: Entry;
|
||||
export const undefinedVarEntry: Entry;
|
||||
|
||||
Reference in New Issue
Block a user