Skip to content

Add unsorted expected completions #1351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 2, 2025
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
8 changes: 7 additions & 1 deletion internal/fourslash/_scripts/convertFourslash.mts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ function parseVerifyCompletionArg(arg: ts.Expression): VerifyCompletionsCmd | un
break;
case "exact":
case "includes":
case "unsorted":
if (init.getText() === "undefined") {
return {
kind: "verifyCompletions",
Expand Down Expand Up @@ -385,9 +386,12 @@ function parseVerifyCompletionArg(arg: ts.Expression): VerifyCompletionsCmd | un
if (propName === "includes") {
(goArgs ??= {}).includes = expected;
}
else {
else if (propName === "exact") {
(goArgs ??= {}).exact = expected;
}
else {
(goArgs ??= {}).unsorted = expected;
}
break;
case "excludes":
let excludes = "[]string{";
Expand Down Expand Up @@ -659,6 +663,7 @@ interface VerifyCompletionsArgs {
includes?: string;
excludes?: string;
exact?: string;
unsorted?: string;
}

interface GoToMarkerCmd {
Expand All @@ -680,6 +685,7 @@ function generateVerifyCompletions({ marker, args, isNewIdentifierLocation }: Ve
if (args.includes) expected.push(`Includes: ${args.includes},`);
if (args.excludes) expected.push(`Excludes: ${args.excludes},`);
if (args.exact) expected.push(`Exact: ${args.exact},`);
if (args.unsorted) expected.push(`Unsorted: ${args.unsorted},`);
// !!! isIncomplete
const commitCharacters = isNewIdentifierLocation ? "[]string{}" : "defaultCommitCharacters";
expectedList = `&fourslash.CompletionsExpectedList{
Expand Down
5 changes: 5 additions & 0 deletions internal/fourslash/_scripts/failingTests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ TestCompletionListAndMemberListOnCommentedWhiteSpace
TestCompletionListAtInvalidLocations
TestCompletionListBuilderLocations_VariableDeclarations
TestCompletionListCladule
TestCompletionListClassMembers
TestCompletionListForExportEquals
TestCompletionListForTransitivelyExportedMembers01
TestCompletionListForTransitivelyExportedMembers04
Expand Down Expand Up @@ -94,6 +95,10 @@ TestCompletionListInUnclosedTemplate01
TestCompletionListInUnclosedTemplate02
TestCompletionListInvalidMemberNames2
TestCompletionListInvalidMemberNames_withExistingIdentifier
TestCompletionListOnAliases2
TestCompletionListPrivateNames
TestCompletionListPrivateNamesAccessors
TestCompletionListPrivateNamesMethods
TestCompletionListStaticMembers
TestCompletionListStaticProtectedMembers
TestCompletionListStaticProtectedMembers2
Expand Down
39 changes: 38 additions & 1 deletion internal/fourslash/fourslash.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package fourslash
import (
"fmt"
"io"
"maps"
"slices"
"strings"
"testing"
"unicode"
Expand Down Expand Up @@ -392,11 +394,11 @@ type CompletionsExpectedItemDefaults struct {
// *lsproto.CompletionItem | string
type CompletionsExpectedItem = any

// !!! unsorted completions
type CompletionsExpectedItems struct {
Includes []CompletionsExpectedItem
Excludes []string
Exact []CompletionsExpectedItem
Unsorted []CompletionsExpectedItem
}

// string | *Marker | []string | []*Marker
Expand Down Expand Up @@ -520,6 +522,9 @@ func verifyCompletionsItems(t *testing.T, prefix string, actual []*lsproto.Compl
if expected.Excludes != nil {
t.Fatal(prefix + "Expected exact completion list but also specified 'excludes'.")
}
if expected.Unsorted != nil {
t.Fatal(prefix + "Expected exact completion list but also specified 'unsorted'.")
}
if len(actual) != len(expected.Exact) {
t.Fatalf(prefix+"Expected %d exact completion items but got %d: %s", len(expected.Exact), len(actual), cmp.Diff(actual, expected.Exact))
}
Expand All @@ -532,6 +537,38 @@ func verifyCompletionsItems(t *testing.T, prefix string, actual []*lsproto.Compl
for _, item := range actual {
nameToActualItem[item.Label] = item
}
if expected.Unsorted != nil {
if expected.Includes != nil {
t.Fatal(prefix + "Expected unsorted completion list but also specified 'includes'.")
}
if expected.Excludes != nil {
t.Fatal(prefix + "Expected unsorted completion list but also specified 'excludes'.")
}
for _, item := range expected.Unsorted {
switch item := item.(type) {
case string:
_, ok := nameToActualItem[item]
if !ok {
t.Fatalf("%sLabel '%s' not found in actual items. Actual items: %s", prefix, item, cmp.Diff(actual, nil))
}
delete(nameToActualItem, item)
case *lsproto.CompletionItem:
actualItem, ok := nameToActualItem[item.Label]
if !ok {
t.Fatalf("%sLabel '%s' not found in actual items. Actual items: %s", prefix, item.Label, cmp.Diff(actual, nil))
}
delete(nameToActualItem, item.Label)
verifyCompletionItem(t, prefix+"Includes completion item mismatch for label "+item.Label, actualItem, item)
default:
t.Fatalf("%sExpected completion item to be a string or *lsproto.CompletionItem, got %T", prefix, item)
}
}
if len(expected.Unsorted) != len(actual) {
unmatched := slices.Collect(maps.Keys(nameToActualItem))
t.Fatalf("%sAdditional completions found but not included in 'unsorted': %s", prefix, strings.Join(unmatched, "\n"))
}
return
}
if expected.Includes != nil {
for _, item := range expected.Includes {
switch item := item.(type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)

func TestCompletionForStringLiteralRelativeImport5(t *testing.T) {
t.Parallel()

defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @rootDirs: /repo/src1,/repo/src2/,/repo/generated1,/repo/generated2/
// @Filename: /dir/secret_file.ts
/*secret_file*/
// @Filename: /repo/src1/dir/test1.ts
import * as foo1 from ".//*import_as1*/
import foo2 = require(".//*import_equals1*/
var foo3 = require(".//*require1*/
// @Filename: /repo/src2/dir/test2.ts
import * as foo1 from "..//*import_as2*/
import foo2 = require("..//*import_equals2*/
var foo3 = require("..//*require2*/
// @Filename: /repo/src2/index.ts
import * as foo1 from ".//*import_as3*/
import foo2 = require(".//*import_equals3*/
var foo3 = require(".//*require3*/
// @Filename: /repo/generated1/dir/f1.ts
/*f1*/
// @Filename: /repo/generated2/dir/f2.ts
/*f2*/`
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
f.VerifyCompletions(t, []string{"import_as1", "import_equals1", "require1"}, &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &[]string{},
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Unsorted: []fourslash.CompletionsExpectedItem{"f1", "f2", "test2"},
},
})
f.VerifyCompletions(t, []string{"import_as2", "import_equals2", "require2"}, &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &[]string{},
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Unsorted: []fourslash.CompletionsExpectedItem{"dir", "index"},
},
})
f.VerifyCompletions(t, []string{"import_as3", "import_equals3", "require3"}, &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &[]string{},
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Unsorted: []fourslash.CompletionsExpectedItem{"dir"},
},
})
}
78 changes: 78 additions & 0 deletions internal/fourslash/tests/gen/completionListClassMembers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)

func TestCompletionListClassMembers(t *testing.T) {
t.Parallel()
t.Skip()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `class Class {
private privateInstanceMethod() { }
public publicInstanceMethod() { }

private privateProperty = 1;
public publicProperty = 1;

private static privateStaticProperty = 1;
public static publicStaticProperty = 1;

private static privateStaticMethod() { }
public static publicStaticMethod() {
Class./*staticsInsideClassScope*/publicStaticMethod();
var c = new Class();
c./*instanceMembersInsideClassScope*/privateProperty;
}
}

Class./*staticsOutsideClassScope*/publicStaticMethod();
var c = new Class();
c./*instanceMembersOutsideClassScope*/privateProperty;`
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
f.VerifyCompletions(t, "staticsInsideClassScope", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: completionFunctionMembersPlus([]fourslash.CompletionsExpectedItem{&lsproto.CompletionItem{SortText: ptrTo(string(ls.SortTextLocalDeclarationPriority)), Label: "privateStaticMethod"}, &lsproto.CompletionItem{SortText: ptrTo(string(ls.SortTextLocalDeclarationPriority)), Label: "privateStaticProperty"}, &lsproto.CompletionItem{SortText: ptrTo(string(ls.SortTextLocalDeclarationPriority)), Label: "publicStaticMethod"}, &lsproto.CompletionItem{SortText: ptrTo(string(ls.SortTextLocalDeclarationPriority)), Label: "publicStaticProperty"}, &lsproto.CompletionItem{SortText: ptrTo(string(ls.SortTextLocationPriority)), Label: "prototype"}}),
},
})
f.VerifyCompletions(t, "instanceMembersInsideClassScope", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Unsorted: []fourslash.CompletionsExpectedItem{"privateInstanceMethod", "publicInstanceMethod", "privateProperty", "publicProperty"},
},
})
f.VerifyCompletions(t, "staticsOutsideClassScope", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: completionFunctionMembersPlus([]fourslash.CompletionsExpectedItem{&lsproto.CompletionItem{SortText: ptrTo(string(ls.SortTextLocalDeclarationPriority)), Label: "publicStaticMethod"}, &lsproto.CompletionItem{SortText: ptrTo(string(ls.SortTextLocalDeclarationPriority)), Label: "publicStaticProperty"}, &lsproto.CompletionItem{SortText: ptrTo(string(ls.SortTextLocationPriority)), Label: "prototype"}}),
},
})
f.VerifyCompletions(t, "instanceMembersOutsideClassScope", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: []fourslash.CompletionsExpectedItem{"publicInstanceMethod", "publicProperty"},
},
})
}
43 changes: 43 additions & 0 deletions internal/fourslash/tests/gen/completionListEnumMembers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)

func TestCompletionListEnumMembers(t *testing.T) {
t.Parallel()

defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `enum Foo {
bar,
baz
}

var v = Foo./*valueReference*/ba;
var t :Foo./*typeReference*/ba;
Foo.bar./*enumValueReference*/;`
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
f.VerifyCompletions(t, []string{"valueReference", "typeReference"}, &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: []fourslash.CompletionsExpectedItem{"bar", "baz"},
},
})
f.VerifyCompletions(t, "enumValueReference", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Unsorted: []fourslash.CompletionsExpectedItem{"toString", "toFixed", "toExponential", "toPrecision", "valueOf", "toLocaleString"},
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)

func TestCompletionListInObjectBindingPattern15(t *testing.T) {
t.Parallel()

defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `class Foo {
private xxx1 = 1;
protected xxx2 = 2;
public xxx3 = 3;
private static xxx4 = 4;
protected static xxx5 = 5;
public static xxx6 = 6;
foo() {
const { /*1*/ } = this;
const { /*2*/ } = Foo;
}
}

const { /*3*/ } = new Foo();
const { /*4*/ } = Foo;`
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Unsorted: []fourslash.CompletionsExpectedItem{"xxx1", "xxx2", "xxx3", "foo"},
},
})
f.VerifyCompletions(t, "2", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Unsorted: []fourslash.CompletionsExpectedItem{"prototype", "xxx4", "xxx5", "xxx6"},
},
})
f.VerifyCompletions(t, "3", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Unsorted: []fourslash.CompletionsExpectedItem{"xxx3", "foo"},
},
})
f.VerifyCompletions(t, "4", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
EditRange: ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Unsorted: []fourslash.CompletionsExpectedItem{"prototype", "xxx6"},
},
})
}
Loading