Strcter testing for global completions; and 'excludes' can only be a string (#28349)

This commit is contained in:
Andy
2018-11-05 16:16:28 -08:00
committed by GitHub
parent 597aacad88
commit c22c7e687d
4 changed files with 316 additions and 276 deletions

View File

@@ -787,6 +787,7 @@ namespace FourSlash {
}
if (options.excludes) {
for (const exclude of toArray(options.excludes)) {
assert(typeof exclude === "string");
if (nameToEntries.has(exclude)) {
this.raiseError(`Did not expect to get a completion named ${exclude}`);
}
@@ -812,7 +813,9 @@ namespace FourSlash {
}
if (kind !== undefined || kindModifiers !== undefined) {
assert.equal(actual.kind, kind);
if (actual.kind !== kind) {
this.raiseError(`Unexpected kind for ${actual.name}: Expected ${kind}, actual ${actual.kind}`);
}
if (actual.kindModifiers !== (kindModifiers || "")) {
this.raiseError(`Bad kind modifiers for ${actual.name}: Expected ${kindModifiers || ""}, actual ${actual.kindModifiers}`);
}
@@ -4427,110 +4430,119 @@ namespace FourSlashInterface {
}
}
export namespace Completion {
const res: string[] = [];
const functionEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "function", kindModifiers: "declare" });
const constEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "const", kindModifiers: "declare" });
const moduleEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "module", kindModifiers: "declare" });
const keywordEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "keyword" });
const methodEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "method", kindModifiers: "declare" });
const propertyEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "property", kindModifiers: "declare" });
const interfaceEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "interface", kindModifiers: "declare" });
const typeEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "type", kindModifiers: "declare" });
const res: ExpectedCompletionEntryObject[] = [];
for (let i = ts.SyntaxKind.FirstKeyword; i <= ts.SyntaxKind.LastKeyword; i++) {
res.push(ts.Debug.assertDefined(ts.tokenToString(i)));
res.push({ name: ts.Debug.assertDefined(ts.tokenToString(i)), kind: "keyword" });
}
export const keywordsWithUndefined: ReadonlyArray<string> = res;
export const keywords: ReadonlyArray<string> = keywordsWithUndefined.filter(k => k !== "undefined");
export const keywordsWithUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = res;
export const keywords: ReadonlyArray<ExpectedCompletionEntryObject> = keywordsWithUndefined.filter(k => k.name !== "undefined");
export const typeKeywords: ReadonlyArray<string> =
["false", "null", "true", "void", "any", "boolean", "keyof", "never", "number", "object", "string", "symbol", "undefined", "unique", "unknown"];
export const typeKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
["false", "null", "true", "void", "any", "boolean", "keyof", "never", "number", "object", "string", "symbol", "undefined", "unique", "unknown"].map(keywordEntry);
const globalTypeDecls = [
"Symbol",
"PropertyKey",
"PropertyDescriptor",
"PropertyDescriptorMap",
"Object",
"ObjectConstructor",
"Function",
"FunctionConstructor",
"CallableFunction",
"NewableFunction",
"IArguments",
"String",
"StringConstructor",
"Boolean",
"BooleanConstructor",
"Number",
"NumberConstructor",
"TemplateStringsArray",
"ImportMeta",
"Math",
"Date",
"DateConstructor",
"RegExpMatchArray",
"RegExpExecArray",
"RegExp",
"RegExpConstructor",
"Error",
"ErrorConstructor",
"EvalError",
"EvalErrorConstructor",
"RangeError",
"RangeErrorConstructor",
"ReferenceError",
"ReferenceErrorConstructor",
"SyntaxError",
"SyntaxErrorConstructor",
"TypeError",
"TypeErrorConstructor",
"URIError",
"URIErrorConstructor",
"JSON",
"ReadonlyArray",
"ConcatArray",
"Array",
"ArrayConstructor",
"TypedPropertyDescriptor",
"ClassDecorator",
"PropertyDecorator",
"MethodDecorator",
"ParameterDecorator",
"PromiseConstructorLike",
"PromiseLike",
"Promise",
"ArrayLike",
"Partial",
"Required",
"Readonly",
"Pick",
"Record",
"Exclude",
"Extract",
"NonNullable",
"Parameters",
"ConstructorParameters",
"ReturnType",
"InstanceType",
"ThisType",
"ArrayBuffer",
"ArrayBufferTypes",
"ArrayBufferLike",
"ArrayBufferConstructor",
"ArrayBufferView",
"DataView",
"DataViewConstructor",
"Int8Array",
"Int8ArrayConstructor",
"Uint8Array",
"Uint8ArrayConstructor",
"Uint8ClampedArray",
"Uint8ClampedArrayConstructor",
"Int16Array",
"Int16ArrayConstructor",
"Uint16Array",
"Uint16ArrayConstructor",
"Int32Array",
"Int32ArrayConstructor",
"Uint32Array",
"Uint32ArrayConstructor",
"Float32Array",
"Float32ArrayConstructor",
"Float64Array",
"Float64ArrayConstructor",
"Intl",
const globalTypeDecls: ReadonlyArray<ExpectedCompletionEntryObject> = [
interfaceEntry("Symbol"),
typeEntry("PropertyKey"),
interfaceEntry("PropertyDescriptor"),
interfaceEntry("PropertyDescriptorMap"),
constEntry("Object"),
interfaceEntry("ObjectConstructor"),
constEntry("Function"),
interfaceEntry("FunctionConstructor"),
interfaceEntry("CallableFunction"),
interfaceEntry("NewableFunction"),
interfaceEntry("IArguments"),
constEntry("String"),
interfaceEntry("StringConstructor"),
constEntry("Boolean"),
interfaceEntry("BooleanConstructor"),
constEntry("Number"),
interfaceEntry("NumberConstructor"),
interfaceEntry("TemplateStringsArray"),
interfaceEntry("ImportMeta"),
constEntry("Math"),
constEntry("Date"),
interfaceEntry("DateConstructor"),
interfaceEntry("RegExpMatchArray"),
interfaceEntry("RegExpExecArray"),
constEntry("RegExp"),
interfaceEntry("RegExpConstructor"),
constEntry("Error"),
interfaceEntry("ErrorConstructor"),
constEntry("EvalError"),
interfaceEntry("EvalErrorConstructor"),
constEntry("RangeError"),
interfaceEntry("RangeErrorConstructor"),
constEntry("ReferenceError"),
interfaceEntry("ReferenceErrorConstructor"),
constEntry("SyntaxError"),
interfaceEntry("SyntaxErrorConstructor"),
constEntry("TypeError"),
interfaceEntry("TypeErrorConstructor"),
constEntry("URIError"),
interfaceEntry("URIErrorConstructor"),
constEntry("JSON"),
interfaceEntry("ReadonlyArray"),
interfaceEntry("ConcatArray"),
constEntry("Array"),
interfaceEntry("ArrayConstructor"),
interfaceEntry("TypedPropertyDescriptor"),
typeEntry("ClassDecorator"),
typeEntry("PropertyDecorator"),
typeEntry("MethodDecorator"),
typeEntry("ParameterDecorator"),
typeEntry("PromiseConstructorLike"),
interfaceEntry("PromiseLike"),
interfaceEntry("Promise"),
interfaceEntry("ArrayLike"),
typeEntry("Partial"),
typeEntry("Required"),
typeEntry("Readonly"),
typeEntry("Pick"),
typeEntry("Record"),
typeEntry("Exclude"),
typeEntry("Extract"),
typeEntry("NonNullable"),
typeEntry("Parameters"),
typeEntry("ConstructorParameters"),
typeEntry("ReturnType"),
typeEntry("InstanceType"),
interfaceEntry("ThisType"),
constEntry("ArrayBuffer"),
interfaceEntry("ArrayBufferTypes"),
typeEntry("ArrayBufferLike"),
interfaceEntry("ArrayBufferConstructor"),
interfaceEntry("ArrayBufferView"),
constEntry("DataView"),
interfaceEntry("DataViewConstructor"),
constEntry("Int8Array"),
interfaceEntry("Int8ArrayConstructor"),
constEntry("Uint8Array"),
interfaceEntry("Uint8ArrayConstructor"),
constEntry("Uint8ClampedArray"),
interfaceEntry("Uint8ClampedArrayConstructor"),
constEntry("Int16Array"),
interfaceEntry("Int16ArrayConstructor"),
constEntry("Uint16Array"),
interfaceEntry("Uint16ArrayConstructor"),
constEntry("Int32Array"),
interfaceEntry("Int32ArrayConstructor"),
constEntry("Uint32Array"),
interfaceEntry("Uint32ArrayConstructor"),
constEntry("Float32Array"),
interfaceEntry("Float32ArrayConstructor"),
constEntry("Float64Array"),
interfaceEntry("Float64ArrayConstructor"),
moduleEntry("Intl"),
];
export const globalTypes = globalTypesPlus([]);
@@ -4543,54 +4555,54 @@ namespace FourSlashInterface {
];
}
export const classElementKeywords: ReadonlyArray<string> =
["private", "protected", "public", "static", "abstract", "async", "constructor", "get", "readonly", "set"];
export const classElementKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
["private", "protected", "public", "static", "abstract", "async", "constructor", "get", "readonly", "set"].map(keywordEntry);
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntry> =
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntry => ({ name, kind: "keyword" }));
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({ name, kind: "keyword" }));
export const functionMembers: ReadonlyArray<ExpectedCompletionEntry> = [
"apply",
"call",
"bind",
"toString",
"length",
{ name: "arguments", text: "(property) Function.arguments: any" },
"caller"
export const functionMembers: ReadonlyArray<ExpectedCompletionEntryObject> = [
methodEntry("apply"),
methodEntry("call"),
methodEntry("bind"),
methodEntry("toString"),
propertyEntry("length"),
{ name: "arguments", kind: "property", kindModifiers: "declare", text: "(property) Function.arguments: any" },
propertyEntry("caller"),
];
export const stringMembers: ReadonlyArray<ExpectedCompletionEntry> = [
"toString",
"charAt",
"charCodeAt",
"concat",
"indexOf",
"lastIndexOf",
"localeCompare",
"match",
"replace",
"search",
"slice",
"split",
"substring",
"toLowerCase",
"toLocaleLowerCase",
"toUpperCase",
"toLocaleUpperCase",
"trim",
"length",
"substr",
"valueOf",
export const stringMembers: ReadonlyArray<ExpectedCompletionEntryObject> = [
methodEntry("toString"),
methodEntry("charAt"),
methodEntry("charCodeAt"),
methodEntry("concat"),
methodEntry("indexOf"),
methodEntry("lastIndexOf"),
methodEntry("localeCompare"),
methodEntry("match"),
methodEntry("replace"),
methodEntry("search"),
methodEntry("slice"),
methodEntry("split"),
methodEntry("substring"),
methodEntry("toLowerCase"),
methodEntry("toLocaleLowerCase"),
methodEntry("toUpperCase"),
methodEntry("toLocaleUpperCase"),
methodEntry("trim"),
propertyEntry("length"),
methodEntry("substr"),
methodEntry("valueOf"),
];
export const functionMembersWithPrototype: ReadonlyArray<ExpectedCompletionEntry> = [
export const functionMembersWithPrototype: ReadonlyArray<ExpectedCompletionEntryObject> = [
...functionMembers.slice(0, 4),
"prototype",
propertyEntry("prototype"),
...functionMembers.slice(4),
];
// TODO: Shouldn't propose type keywords in statement position
export const statementKeywordsWithTypes: ReadonlyArray<string> = [
export const statementKeywordsWithTypes: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
"case",
"catch",
@@ -4665,108 +4677,125 @@ namespace FourSlashInterface {
"global",
"bigint",
"of",
].map(keywordEntry);
export const statementKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = statementKeywordsWithTypes.filter(k => {
const name = k.name;
switch (name) {
case "false":
case "true":
case "null":
case "void":
return true;
case "declare":
case "module":
return false;
default:
return !ts.contains(typeKeywords, k);
}
});
export const globalsVars: ReadonlyArray<ExpectedCompletionEntryObject> = [
functionEntry("eval"),
functionEntry("parseInt"),
functionEntry("parseFloat"),
functionEntry("isNaN"),
functionEntry("isFinite"),
functionEntry("decodeURI"),
functionEntry("decodeURIComponent"),
functionEntry("encodeURI"),
functionEntry("encodeURIComponent"),
functionEntry("escape"),
functionEntry("unescape"),
constEntry("NaN"),
constEntry("Infinity"),
constEntry("Object"),
constEntry("Function"),
constEntry("String"),
constEntry("Boolean"),
constEntry("Number"),
constEntry("Math"),
constEntry("Date"),
constEntry("RegExp"),
constEntry("Error"),
constEntry("EvalError"),
constEntry("RangeError"),
constEntry("ReferenceError"),
constEntry("SyntaxError"),
constEntry("TypeError"),
constEntry("URIError"),
constEntry("JSON"),
constEntry("Array"),
constEntry("ArrayBuffer"),
constEntry("DataView"),
constEntry("Int8Array"),
constEntry("Uint8Array"),
constEntry("Uint8ClampedArray"),
constEntry("Int16Array"),
constEntry("Uint16Array"),
constEntry("Int32Array"),
constEntry("Uint32Array"),
constEntry("Float32Array"),
constEntry("Float64Array"),
moduleEntry("Intl"),
];
export const statementKeywords: ReadonlyArray<string> = statementKeywordsWithTypes.filter(k =>
k === "false" || k === "true" || k === "null" || k === "void" || !ts.contains(typeKeywords, k) && k !== "declare" && k !== "module");
export const globalsVars: ReadonlyArray<string> = [
"eval",
"parseInt",
"parseFloat",
"isNaN",
"isFinite",
"decodeURI",
"decodeURIComponent",
"encodeURI",
"encodeURIComponent",
"escape",
"unescape",
"NaN",
"Infinity",
"Object",
"Function",
"String",
"Boolean",
"Number",
"Math",
"Date",
"RegExp",
"Error",
"EvalError",
"RangeError",
"ReferenceError",
"SyntaxError",
"TypeError",
"URIError",
"JSON",
"Array",
"ArrayBuffer",
"DataView",
"Int8Array",
"Uint8Array",
"Uint8ClampedArray",
"Int16Array",
"Uint16Array",
"Int32Array",
"Uint32Array",
"Float32Array",
"Float64Array",
"Intl",
];
const globalKeywordsInsideFunction: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"enum",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"null",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with",
"implements",
"interface",
"let",
"package",
"yield",
"async",
].map(keywordEntry);
// TODO: many of these are inappropriate to always provide
export const globalsInsideFunction = (plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> => [
"arguments",
{ name: "arguments", kind: "local var" },
...plus,
...globalsVars,
"undefined",
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"enum",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"null",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with",
"implements",
"interface",
"let",
"package",
"yield",
"async",
{ name: "undefined", kind: "var" },
...globalKeywordsInsideFunction,
];
// TODO: many of these are inappropriate to always provide
export const globalKeywords: ReadonlyArray<string> = [
export const globalKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
"case",
"catch",
@@ -4841,9 +4870,9 @@ namespace FourSlashInterface {
"global",
"bigint",
"of",
];
].map(keywordEntry);
export const insideMethodKeywords: ReadonlyArray<string> = [
export const insideMethodKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
"case",
"catch",
@@ -4886,17 +4915,21 @@ namespace FourSlashInterface {
"package",
"yield",
"async",
];
].map(keywordEntry);
export const globalKeywordsPlusUndefined: ReadonlyArray<string> = (() => {
const i = globalKeywords.indexOf("unique");
return [...globalKeywords.slice(0, i), "undefined", ...globalKeywords.slice(i)];
export const globalKeywordsPlusUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = (() => {
const i = ts.findIndex(globalKeywords, x => x.name === "unique");
return [...globalKeywords.slice(0, i), keywordEntry("undefined"), ...globalKeywords.slice(i)];
})();
export const globals: ReadonlyArray<string> = [...globalsVars, "undefined", ...globalKeywords];
export const globals: ReadonlyArray<ExpectedCompletionEntryObject> = [
...globalsVars,
{ name: "undefined", kind: "var" },
...globalKeywords
];
export function globalsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
return [...globalsVars, ...plus, "undefined", ...globalKeywords];
return [...globalsVars, ...plus, { name: "undefined", kind: "var" }, ...globalKeywords];
}
}
@@ -4914,20 +4947,21 @@ namespace FourSlashInterface {
newContent: NewFileContent;
}
export type ExpectedCompletionEntry = string | {
readonly name: string,
readonly source?: string,
readonly insertText?: string,
readonly replacementSpan?: FourSlash.Range,
readonly hasAction?: boolean, // If not specified, will assert that this is false.
export type ExpectedCompletionEntry = string | ExpectedCompletionEntryObject;
export interface ExpectedCompletionEntryObject {
readonly name: string;
readonly source?: string;
readonly insertText?: string;
readonly replacementSpan?: FourSlash.Range;
readonly hasAction?: boolean; // If not specified, will assert that this is false.
readonly isRecommended?: boolean; // If not specified, will assert that this is false.
readonly kind?: string, // If not specified, won't assert about this
readonly kindModifiers?: string, // Must be paired with 'kind'
readonly kind?: string; // If not specified, won't assert about this
readonly kindModifiers?: string; // Must be paired with 'kind'
readonly text?: string;
readonly documentation?: string;
readonly sourceDisplay?: string;
readonly tags?: ReadonlyArray<ts.JSDocTagInfo>;
};
}
export interface VerifyCompletionsOptions {
readonly marker?: ArrayOrSingle<string | FourSlash.Marker>;

View File

@@ -21,22 +21,23 @@ verify.completions(
// no reserved words
{
marker: "break",
includes: { name: "break", text: "break", kind: "keyword" },
excludes: { name: "break", source: "/a" },
exact: completion.globals,
preferences,
},
// no strict mode reserved words
{
marker: "implements",
includes: { name: "implements", text: "implements", kind: "keyword" },
excludes: { name: "implements", source: "/a" },
exact: completion.globals,
preferences,
},
// yes contextual keywords
{
marker: "unique",
includes: { name: "unique", source: "/a", sourceDisplay: "./a", text: "(alias) const unique: 0\nexport unique", hasAction: true },
excludes: { name: "unique", source: undefined },
exact: [
...completion.globalsVars, "undefined",
{ name: "unique", source: "/a", sourceDisplay: "./a", text: "(alias) const unique: 0\nexport unique", hasAction: true },
...completion.globalKeywords.filter(e => e.name !== "unique"),
],
preferences,
},
);

View File

@@ -514,7 +514,7 @@ declare namespace FourSlashInterface {
readonly isGlobalCompletion?: boolean;
readonly exact?: ArrayOrSingle<ExpectedCompletionEntry>;
readonly includes?: ArrayOrSingle<ExpectedCompletionEntry>;
readonly excludes?: ArrayOrSingle<string | { name: string, source: string }>;
readonly excludes?: ArrayOrSingle<string>;
readonly preferences?: UserPreferences;
readonly triggerCharacter?: string;
}
@@ -648,23 +648,24 @@ declare var format: FourSlashInterface.format;
declare var cancellation: FourSlashInterface.cancellation;
declare var classification: typeof FourSlashInterface.classification;
declare namespace completion {
export const globals: ReadonlyArray<string>;
export const globalKeywords: ReadonlyArray<string>;
export const insideMethodKeywords: ReadonlyArray<string>;
export const globalKeywordsPlusUndefined: ReadonlyArray<string>;
export const globalsVars: ReadonlyArray<string>;
export function globalsInsideFunction(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>;
export function globalsPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>;
export const keywordsWithUndefined: ReadonlyArray<string>;
export const keywords: ReadonlyArray<string>;
export const typeKeywords: ReadonlyArray<string>;
export const globalTypes: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>;
export function globalTypesPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>;
export const classElementKeywords: ReadonlyArray<string>;
export const constructorParameterKeywords: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>;
export const functionMembers: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>;
export const stringMembers: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>;
export const functionMembersWithPrototype: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>;
export const statementKeywordsWithTypes: ReadonlyArray<string>;
export const statementKeywords: ReadonlyArray<string>;
type Entry = FourSlashInterface.ExpectedCompletionEntryObject;
export const globals: ReadonlyArray<Entry>;
export const globalKeywords: ReadonlyArray<Entry>;
export const insideMethodKeywords: ReadonlyArray<Entry>;
export const globalKeywordsPlusUndefined: ReadonlyArray<Entry>;
export const globalsVars: ReadonlyArray<Entry>;
export function globalsInsideFunction(plus: ReadonlyArray<Entry>): ReadonlyArray<Entry>;
export function globalsPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<Entry>;
export const keywordsWithUndefined: ReadonlyArray<Entry>;
export const keywords: ReadonlyArray<Entry>;
export const typeKeywords: ReadonlyArray<Entry>;
export const globalTypes: ReadonlyArray<Entry>;
export function globalTypesPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<Entry>;
export const classElementKeywords: ReadonlyArray<Entry>;
export const constructorParameterKeywords: ReadonlyArray<Entry>;
export const functionMembers: ReadonlyArray<Entry>;
export const stringMembers: ReadonlyArray<Entry>;
export const functionMembersWithPrototype: ReadonlyArray<Entry>;
export const statementKeywordsWithTypes: ReadonlyArray<Entry>;
export const statementKeywords: ReadonlyArray<Entry>;
}

View File

@@ -7,9 +7,13 @@
////}
////Foo./**/
const entryName = (e: FourSlashInterface.ExpectedCompletionEntry) => typeof e === "string" ? e : e.name;
const sortedFunctionMembers = completion.functionMembersWithPrototype.slice().sort((a, b) => entryName(a).localeCompare(entryName(b)));
const exact: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry> = [...sortedFunctionMembers, { name: "x", text: "var Foo.x: number" }];
const sortedFunctionMembers = completion.functionMembersWithPrototype.slice().sort((a, b) => a.name.localeCompare(b.name));
const exact: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry> = [
...sortedFunctionMembers.map(e =>
e.name === "arguments" ? { ...e, kind: "property", kindModifiers: "declare" } :
e.name === "prototype" ? { ...e, kindModifiers: undefined } : e),
{ name: "x", text: "var Foo.x: number" },
];
verify.completions({ marker: "", exact });
// Make an edit