mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 10:46:28 -05:00
Always use verify.completions when testing completions (#28137)
This commit is contained in:
@@ -729,97 +729,6 @@ namespace FourSlash {
|
||||
});
|
||||
}
|
||||
|
||||
public verifyCompletionListCount(expectedCount: number, negative: boolean) {
|
||||
if (expectedCount === 0 && negative) {
|
||||
this.verifyCompletionListIsEmpty(/*negative*/ false);
|
||||
return;
|
||||
}
|
||||
|
||||
const members = this.getCompletionListAtCaret();
|
||||
|
||||
if (members) {
|
||||
const match = members.entries.length === expectedCount;
|
||||
|
||||
if ((!match && !negative) || (match && negative)) {
|
||||
this.raiseError("Member list count was " + members.entries.length + ". Expected " + expectedCount);
|
||||
}
|
||||
}
|
||||
else if (expectedCount) {
|
||||
this.raiseError("Member list count was 0. Expected " + expectedCount);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionListItemsCountIsGreaterThan(count: number, negative: boolean) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
const itemsCount = completions ? completions.entries.length : 0;
|
||||
|
||||
if (negative) {
|
||||
if (itemsCount > count) {
|
||||
this.raiseError(`Expected completion list items count to not be greater than ${count}, but is actually ${itemsCount}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (itemsCount <= count) {
|
||||
this.raiseError(`Expected completion list items count to be greater than ${count}, but is actually ${itemsCount}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionListStartsWithItemsInOrder(items: string[]): void {
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entries = this.getCompletionListAtCaret()!.entries;
|
||||
assert.isTrue(items.length <= entries.length, `Amount of expected items in completion list [ ${items.length} ] is greater than actual number of items in list [ ${entries.length} ]`);
|
||||
ts.zipWith(entries, items, (entry, item) => {
|
||||
assert.equal(entry.name, item, `Unexpected item in completion list`);
|
||||
});
|
||||
}
|
||||
|
||||
public noItemsWithSameNameButDifferentKind(): void {
|
||||
const completions = this.getCompletionListAtCaret()!;
|
||||
const uniqueItems = ts.createMap<string>();
|
||||
for (const item of completions.entries) {
|
||||
const uniqueItem = uniqueItems.get(item.name);
|
||||
if (!uniqueItem) {
|
||||
uniqueItems.set(item.name, item.kind);
|
||||
}
|
||||
else {
|
||||
assert.equal(item.kind, uniqueItem, `Items should have the same kind, got ${item.kind} and ${uniqueItem}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionListIsEmpty(negative: boolean) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
if ((!completions || completions.entries.length === 0) && negative) {
|
||||
this.raiseError("Completion list is empty at caret at position " + this.activeFile.fileName + " " + this.currentCaretPosition);
|
||||
}
|
||||
else if (completions && completions.entries.length !== 0 && !negative) {
|
||||
this.raiseError(`Completion list is not empty at caret at position ${this.activeFile.fileName} ${this.currentCaretPosition}\n` +
|
||||
`Completion List contains: ${stringify(completions.entries.map(e => e.name))}`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionListAllowsNewIdentifier(negative: boolean) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
|
||||
if ((completions && !completions.isNewIdentifierLocation) && !negative) {
|
||||
this.raiseError("Expected builder completion entry");
|
||||
}
|
||||
else if ((completions && completions.isNewIdentifierLocation) && negative) {
|
||||
this.raiseError("Un-expected builder completion entry");
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionListIsGlobal(expected: boolean) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
if (completions && completions.isGlobalCompletion !== expected) {
|
||||
this.raiseError(`verifyCompletionListIsGlobal failed - expected result to be ${completions.isGlobalCompletion}`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletions(options: FourSlashInterface.VerifyCompletionsOptions) {
|
||||
if (options.marker === undefined) {
|
||||
this.verifyCompletionsWorker(options);
|
||||
@@ -843,13 +752,21 @@ namespace FourSlash {
|
||||
this.raiseError(`Expected 'isNewIdentifierLocation' to be ${options.isNewIdentifierLocation || false}, got ${actualCompletions.isNewIdentifierLocation}`);
|
||||
}
|
||||
|
||||
const actualByName = ts.createMap<ts.CompletionEntry>();
|
||||
if ("isGlobalCompletion" in options && actualCompletions.isGlobalCompletion !== options.isGlobalCompletion) {
|
||||
this.raiseError(`Expected 'isGlobalCompletion to be ${options.isGlobalCompletion}, got ${actualCompletions.isGlobalCompletion}`);
|
||||
}
|
||||
|
||||
const nameToEntries = ts.createMap<ts.CompletionEntry[]>();
|
||||
for (const entry of actualCompletions.entries) {
|
||||
if (actualByName.has(entry.name)) {
|
||||
this.raiseError(`Duplicate (${actualCompletions.entries.filter(a => a.name === entry.name).length}) completions for ${entry.name}`);
|
||||
const entries = nameToEntries.get(entry.name);
|
||||
if (!entries) {
|
||||
nameToEntries.set(entry.name, [entry]);
|
||||
}
|
||||
else {
|
||||
actualByName.set(entry.name, entry);
|
||||
if (entries.some(e => e.source === entry.source)) {
|
||||
this.raiseError(`Duplicate completions for ${entry.name}`);
|
||||
}
|
||||
entries.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,23 +779,16 @@ namespace FourSlash {
|
||||
if (options.includes) {
|
||||
for (const include of toArray(options.includes)) {
|
||||
const name = typeof include === "string" ? include : include.name;
|
||||
const found = actualByName.get(name);
|
||||
const found = nameToEntries.get(name);
|
||||
if (!found) throw this.raiseError(`No completion ${name} found`);
|
||||
this.verifyCompletionEntry(found, include);
|
||||
assert(found.length === 1); // Must use 'exact' for multiple completions with same name
|
||||
this.verifyCompletionEntry(ts.first(found), include);
|
||||
}
|
||||
}
|
||||
if (options.excludes) {
|
||||
for (const exclude of toArray(options.excludes)) {
|
||||
if (typeof exclude === "string") {
|
||||
if (actualByName.has(exclude)) {
|
||||
this.raiseError(`Did not expect to get a completion named ${exclude}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const found = actualByName.get(exclude.name);
|
||||
if (found && found.source === exclude.source) {
|
||||
this.raiseError(`Did not expect to get a completion named ${exclude.name} with source ${exclude.source}`);
|
||||
}
|
||||
if (nameToEntries.has(exclude)) {
|
||||
this.raiseError(`Did not expect to get a completion named ${exclude}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -902,6 +812,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
if (kind !== undefined) assert.equal(actual.kind, kind);
|
||||
if (typeof expected !== "string" && "kindModifiers" in expected) assert.equal(actual.kindModifiers, expected.kindModifiers);
|
||||
|
||||
assert.equal(actual.hasAction, hasAction);
|
||||
assert.equal(actual.isRecommended, isRecommended);
|
||||
@@ -923,9 +834,8 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
private verifyCompletionsAreExactly(actual: ReadonlyArray<ts.CompletionEntry>, expected: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>) {
|
||||
if (actual.length !== expected.length) {
|
||||
this.raiseError(`Expected ${expected.length} completions, got ${actual.length} (${actual.map(a => a.name)}).`);
|
||||
}
|
||||
// First pass: test that names are right. Then we'll test details.
|
||||
assert.deepEqual(actual.map(a => a.name), expected.map(e => typeof e === "string" ? e : e.name));
|
||||
|
||||
ts.zipWith(actual, expected, (completion, expectedCompletion, index) => {
|
||||
const name = typeof expectedCompletion === "string" ? expectedCompletion : expectedCompletion.name;
|
||||
@@ -936,117 +846,6 @@ namespace FourSlash {
|
||||
});
|
||||
}
|
||||
|
||||
public verifyCompletionsAt(markerName: string | ReadonlyArray<string>, expected: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>, options?: FourSlashInterface.CompletionsAtOptions) {
|
||||
this.verifyCompletions({
|
||||
marker: markerName,
|
||||
exact: expected,
|
||||
isNewIdentifierLocation: options && options.isNewIdentifierLocation,
|
||||
preferences: options,
|
||||
triggerCharacter: options && options.triggerCharacter,
|
||||
});
|
||||
}
|
||||
|
||||
public verifyCompletionListContains(entryId: ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, hasAction?: boolean, options?: FourSlashInterface.VerifyCompletionListContainsOptions) {
|
||||
const completions = this.getCompletionListAtCaret(options);
|
||||
if (completions) {
|
||||
this.assertItemInCompletionList(completions.entries, entryId, text, documentation, kind, spanIndex, hasAction, options);
|
||||
}
|
||||
else {
|
||||
this.raiseError(`No completions at position '${this.currentCaretPosition}' when looking for '${JSON.stringify(entryId)}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the completion list does NOT contain the given symbol.
|
||||
* The symbol is considered matched with the symbol in the list if and only if all given parameters must matched.
|
||||
* When any parameter is omitted, the parameter is ignored during comparison and assumed that the parameter with
|
||||
* that property of the symbol in the list.
|
||||
* @param symbol the name of symbol
|
||||
* @param expectedText the text associated with the symbol
|
||||
* @param expectedDocumentation the documentation text associated with the symbol
|
||||
* @param expectedKind the kind of symbol (see ScriptElementKind)
|
||||
* @param spanIndex the index of the range that the completion item's replacement text span should match
|
||||
*/
|
||||
public verifyCompletionListDoesNotContain(entryId: ts.Completions.CompletionEntryIdentifier, expectedText?: string, expectedDocumentation?: string, expectedKind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, options?: FourSlashInterface.CompletionsAtOptions) {
|
||||
let replacementSpan: ts.TextSpan | undefined;
|
||||
if (spanIndex !== undefined) {
|
||||
replacementSpan = this.getTextSpanForRangeAtIndex(spanIndex);
|
||||
}
|
||||
|
||||
const completions = this.getCompletionListAtCaret(options);
|
||||
if (completions) {
|
||||
let filterCompletions = completions.entries.filter(e => e.name === entryId.name && e.source === entryId.source);
|
||||
filterCompletions = expectedKind ? filterCompletions.filter(e => e.kind === expectedKind || (typeof expectedKind === "object" && e.kind === expectedKind.kind)) : filterCompletions;
|
||||
filterCompletions = filterCompletions.filter(entry => {
|
||||
const details = this.getCompletionEntryDetails(entry.name);
|
||||
const documentation = details && ts.displayPartsToString(details.documentation);
|
||||
const text = details && ts.displayPartsToString(details.displayParts);
|
||||
|
||||
// If any of the expected values are undefined, assume that users don't
|
||||
// care about them.
|
||||
if (replacementSpan && !ts.textSpansEqual(replacementSpan, entry.replacementSpan)) {
|
||||
return false;
|
||||
}
|
||||
else if (expectedText && text !== expectedText) {
|
||||
return false;
|
||||
}
|
||||
else if (expectedDocumentation && documentation !== expectedDocumentation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
if (filterCompletions.length !== 0) {
|
||||
// After filtered using all present criterion, if there are still symbol left in the list
|
||||
// then these symbols must meet the criterion for Not supposed to be in the list. So we
|
||||
// raise an error
|
||||
let error = `Completion list did contain '${JSON.stringify(entryId)}\'.`;
|
||||
const details = this.getCompletionEntryDetails(filterCompletions[0].name)!;
|
||||
if (expectedText) {
|
||||
error += "Expected text: " + expectedText + " to equal: " + ts.displayPartsToString(details.displayParts) + ".";
|
||||
}
|
||||
if (expectedDocumentation) {
|
||||
error += "Expected documentation: " + expectedDocumentation + " to equal: " + ts.displayPartsToString(details.documentation) + ".";
|
||||
}
|
||||
if (expectedKind) {
|
||||
error += "Expected kind: " + expectedKind + " to equal: " + filterCompletions[0].kind + ".";
|
||||
}
|
||||
else {
|
||||
error += "kind: " + filterCompletions[0].kind + ".";
|
||||
}
|
||||
if (replacementSpan) {
|
||||
const spanText = filterCompletions[0].replacementSpan ? stringify(filterCompletions[0].replacementSpan) : undefined;
|
||||
error += "Expected replacement span: " + stringify(replacementSpan) + " to equal: " + spanText + ".";
|
||||
}
|
||||
this.raiseError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionEntryDetails(entryName: string, expectedText: string, expectedDocumentation?: string, kind?: string, tags?: ts.JSDocTagInfo[]) {
|
||||
const details = this.getCompletionEntryDetails(entryName)!;
|
||||
|
||||
assert(details, "no completion entry available");
|
||||
|
||||
assert.equal(ts.displayPartsToString(details.displayParts), expectedText, this.assertionMessageAtLastKnownMarker("completion entry details text"));
|
||||
|
||||
if (expectedDocumentation !== undefined) {
|
||||
assert.equal(ts.displayPartsToString(details.documentation), expectedDocumentation, this.assertionMessageAtLastKnownMarker("completion entry documentation"));
|
||||
}
|
||||
|
||||
if (kind !== undefined) {
|
||||
assert.equal(details.kind, kind, this.assertionMessageAtLastKnownMarker("completion entry kind"));
|
||||
}
|
||||
|
||||
if (tags !== undefined) {
|
||||
assert.equal(details.tags!.length, tags.length, this.messageAtLastKnownMarker("QuickInfo tags"));
|
||||
ts.zipWith(tags, details.tags!, (expectedTag, actualTag) => {
|
||||
assert.equal(actualTag.name, expectedTag.name);
|
||||
assert.equal(actualTag.text, expectedTag.text, this.messageAtLastKnownMarker("QuickInfo tag " + actualTag.name));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Use `getProgram` instead of accessing this directly. */
|
||||
private _program: ts.Program;
|
||||
/** Use `getChecker` instead of accessing this directly. */
|
||||
@@ -3172,74 +2971,6 @@ Actual: ${stringify(fullActual)}`);
|
||||
return text.substring(startPos, endPos);
|
||||
}
|
||||
|
||||
private assertItemInCompletionList(
|
||||
items: ts.CompletionEntry[],
|
||||
entryId: ts.Completions.CompletionEntryIdentifier,
|
||||
text: string | undefined,
|
||||
documentation: string | undefined,
|
||||
kind: string | undefined | { kind?: string, kindModifiers?: string },
|
||||
spanIndex: number | undefined,
|
||||
hasAction: boolean | undefined,
|
||||
options: FourSlashInterface.VerifyCompletionListContainsOptions | undefined,
|
||||
) {
|
||||
const eq = <T>(a: T, b: T, msg: string) => {
|
||||
assert.deepEqual(a, b, this.assertionMessageAtLastKnownMarker(msg + " for " + stringify(entryId)));
|
||||
};
|
||||
const matchingItems = items.filter(item => item.name === entryId.name && item.source === entryId.source);
|
||||
if (matchingItems.length === 0) {
|
||||
const itemsString = items.map(item => stringify({ name: item.name, source: item.source, kind: item.kind })).join(",\n");
|
||||
this.raiseError(`Expected "${stringify({ entryId, text, documentation, kind })}" to be in list [${itemsString}]`);
|
||||
}
|
||||
else if (matchingItems.length > 1) {
|
||||
this.raiseError(`Found duplicate completion items for ${stringify(entryId)}`);
|
||||
}
|
||||
const item = matchingItems[0];
|
||||
|
||||
if (documentation !== undefined || text !== undefined || entryId.source !== undefined) {
|
||||
const details = this.getCompletionEntryDetails(item.name, item.source)!;
|
||||
|
||||
if (documentation !== undefined) {
|
||||
eq(ts.displayPartsToString(details.documentation), documentation, "completion item documentation");
|
||||
}
|
||||
if (text !== undefined) {
|
||||
eq(ts.displayPartsToString(details.displayParts), text, "completion item detail text");
|
||||
}
|
||||
|
||||
if (entryId.source === undefined) {
|
||||
eq(options && options.sourceDisplay, /*b*/ undefined, "source display");
|
||||
}
|
||||
else {
|
||||
eq(details.source, [ts.textPart(options!.sourceDisplay)], "source display");
|
||||
}
|
||||
}
|
||||
|
||||
if (kind !== undefined) {
|
||||
if (typeof kind === "string") {
|
||||
eq(item.kind, kind, "completion item kind");
|
||||
}
|
||||
else {
|
||||
if (kind.kind) {
|
||||
eq(item.kind, kind.kind, "completion item kind");
|
||||
}
|
||||
if (kind.kindModifiers !== undefined) {
|
||||
eq(item.kindModifiers, kind.kindModifiers, "completion item kindModifiers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (spanIndex !== undefined) {
|
||||
const span = this.getTextSpanForRangeAtIndex(spanIndex);
|
||||
assert.isTrue(ts.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + stringify(entryId)));
|
||||
}
|
||||
|
||||
eq(item.hasAction, hasAction, "hasAction");
|
||||
eq(item.isRecommended, options && options.isRecommended, "isRecommended");
|
||||
eq(item.insertText, options && options.insertText, "insertText");
|
||||
eq(item.replacementSpan, options && options.replacementSpan && ts.createTextSpanFromRange(options.replacementSpan), "replacementSpan");
|
||||
}
|
||||
|
||||
private findFile(indexOrName: string | number): FourSlashFile {
|
||||
if (typeof indexOrName === "number") {
|
||||
const index = indexOrName;
|
||||
@@ -3289,16 +3020,6 @@ Actual: ${stringify(fullActual)}`);
|
||||
return `line ${(pos.line + 1)}, col ${pos.character}`;
|
||||
}
|
||||
|
||||
private getTextSpanForRangeAtIndex(index: number): ts.TextSpan {
|
||||
const ranges = this.getRanges();
|
||||
if (ranges.length > index) {
|
||||
return ts.createTextSpanFromRange(ranges[index]);
|
||||
}
|
||||
else {
|
||||
throw this.raiseError("Supplied span index: " + index + " does not exist in range list of size: " + ranges.length);
|
||||
}
|
||||
}
|
||||
|
||||
public getMarkerByName(markerName: string) {
|
||||
const markerPos = this.testData.markerPositions.get(markerName);
|
||||
if (markerPos === undefined) {
|
||||
@@ -3418,7 +3139,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
function runCode(code: string, state: TestState): void {
|
||||
// Compile and execute the test
|
||||
const wrappedCode =
|
||||
`(function(test, goTo, verify, edit, debug, format, cancellation, classification, verifyOperationIsCancelled) {
|
||||
`(function(test, goTo, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {
|
||||
${code}
|
||||
})`;
|
||||
try {
|
||||
@@ -3430,7 +3151,7 @@ ${code}
|
||||
const format = new FourSlashInterface.Format(state);
|
||||
const cancellation = new FourSlashInterface.Cancellation(state);
|
||||
const f = eval(wrappedCode);
|
||||
f(test, goTo, verify, edit, debug, format, cancellation, FourSlashInterface.Classification, verifyOperationIsCancelled);
|
||||
f(test, goTo, verify, edit, debug, format, cancellation, FourSlashInterface.Classification, FourSlashInterface.Completion, verifyOperationIsCancelled);
|
||||
}
|
||||
catch (err) {
|
||||
throw err;
|
||||
@@ -3997,24 +3718,6 @@ namespace FourSlashInterface {
|
||||
|
||||
export class VerifyNegatable {
|
||||
public not: VerifyNegatable;
|
||||
public allowedClassElementKeywords = [
|
||||
"public",
|
||||
"private",
|
||||
"protected",
|
||||
"static",
|
||||
"abstract",
|
||||
"readonly",
|
||||
"get",
|
||||
"set",
|
||||
"constructor",
|
||||
"async"
|
||||
];
|
||||
public allowedConstructorParameterKeywords = [
|
||||
"public",
|
||||
"private",
|
||||
"protected",
|
||||
"readonly",
|
||||
];
|
||||
|
||||
constructor(protected state: FourSlash.TestState, private negative = false) {
|
||||
if (!negative) {
|
||||
@@ -4022,58 +3725,10 @@ namespace FourSlashInterface {
|
||||
}
|
||||
}
|
||||
|
||||
public completionListCount(expectedCount: number) {
|
||||
this.state.verifyCompletionListCount(expectedCount, this.negative);
|
||||
}
|
||||
|
||||
// Verifies the completion list contains the specified symbol. The
|
||||
// completion list is brought up if necessary
|
||||
public completionListContains(entryId: string | ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, hasAction?: boolean, options?: VerifyCompletionListContainsOptions) {
|
||||
if (typeof entryId === "string") {
|
||||
entryId = { name: entryId, source: undefined };
|
||||
}
|
||||
if (this.negative) {
|
||||
this.state.verifyCompletionListDoesNotContain(entryId, text, documentation, kind, spanIndex, options);
|
||||
}
|
||||
else {
|
||||
this.state.verifyCompletionListContains(entryId, text, documentation, kind, spanIndex, hasAction, options);
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies the completion list items count to be greater than the specified amount. The
|
||||
// completion list is brought up if necessary
|
||||
public completionListItemsCountIsGreaterThan(count: number) {
|
||||
this.state.verifyCompletionListItemsCountIsGreaterThan(count, this.negative);
|
||||
}
|
||||
|
||||
public assertHasRanges(ranges: FourSlash.Range[]) {
|
||||
assert(ranges.length !== 0, "Array of ranges is expected to be non-empty");
|
||||
}
|
||||
|
||||
public completionListIsEmpty() {
|
||||
this.state.verifyCompletionListIsEmpty(this.negative);
|
||||
}
|
||||
|
||||
public completionListContainsClassElementKeywords() {
|
||||
for (const keyword of this.allowedClassElementKeywords) {
|
||||
this.completionListContains(keyword, keyword, /*documentation*/ undefined, "keyword");
|
||||
}
|
||||
}
|
||||
|
||||
public completionListContainsConstructorParameterKeywords() {
|
||||
for (const keyword of this.allowedConstructorParameterKeywords) {
|
||||
this.completionListContains(keyword, keyword, /*documentation*/ undefined, "keyword");
|
||||
}
|
||||
}
|
||||
|
||||
public completionListIsGlobal(expected: boolean) {
|
||||
this.state.verifyCompletionListIsGlobal(expected);
|
||||
}
|
||||
|
||||
public completionListAllowsNewIdentifier() {
|
||||
this.state.verifyCompletionListAllowsNewIdentifier(this.negative);
|
||||
}
|
||||
|
||||
public noSignatureHelp(...markers: string[]): void {
|
||||
this.state.verifySignatureHelpPresence(/*expectPresent*/ false, /*triggerReason*/ undefined, markers);
|
||||
}
|
||||
@@ -4160,10 +3815,6 @@ namespace FourSlashInterface {
|
||||
super(state);
|
||||
}
|
||||
|
||||
public completionsAt(markerName: ArrayOrSingle<string>, completions: ReadonlyArray<ExpectedCompletionEntry>, options?: CompletionsAtOptions) {
|
||||
this.state.verifyCompletionsAt(markerName, completions, options);
|
||||
}
|
||||
|
||||
public completions(...optionsArray: VerifyCompletionsOptions[]) {
|
||||
for (const options of optionsArray) {
|
||||
this.state.verifyCompletions(options);
|
||||
@@ -4413,10 +4064,6 @@ namespace FourSlashInterface {
|
||||
this.state.verifyNoDocumentHighlights(startRange);
|
||||
}
|
||||
|
||||
public completionEntryDetailIs(entryName: string, text: string, documentation?: string, kind?: string, tags?: ts.JSDocTagInfo[]) {
|
||||
this.state.verifyCompletionEntryDetails(entryName, text, documentation, kind, tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method *requires* a contiguous, complete, and ordered stream of classifications for a file.
|
||||
*/
|
||||
@@ -4761,6 +4408,477 @@ namespace FourSlashInterface {
|
||||
return { classificationType, text, textSpan };
|
||||
}
|
||||
}
|
||||
export namespace Completion {
|
||||
const res: string[] = [];
|
||||
for (let i = ts.SyntaxKind.FirstKeyword; i <= ts.SyntaxKind.LastKeyword; i++) {
|
||||
res.push(ts.Debug.assertDefined(ts.tokenToString(i)));
|
||||
}
|
||||
export const keywordsWithUndefined: ReadonlyArray<string> = res;
|
||||
export const keywords: ReadonlyArray<string> = keywordsWithUndefined.filter(k => k !== "undefined");
|
||||
|
||||
export const typeKeywords: ReadonlyArray<string> =
|
||||
["false", "null", "true", "void", "any", "boolean", "keyof", "never", "number", "object", "string", "symbol", "undefined", "unique", "unknown"];
|
||||
|
||||
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",
|
||||
];
|
||||
|
||||
export const globalTypes = globalTypesPlus([]);
|
||||
|
||||
export function globalTypesPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
|
||||
return [
|
||||
...globalTypeDecls,
|
||||
...plus,
|
||||
...typeKeywords,
|
||||
];
|
||||
}
|
||||
|
||||
export const classElementKeywords: ReadonlyArray<string> =
|
||||
["private", "protected", "public", "static", "abstract", "async", "constructor", "get", "readonly", "set"];
|
||||
|
||||
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntry> =
|
||||
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntry => ({ name, kind: "keyword" }));
|
||||
|
||||
export const functionMembers: ReadonlyArray<ExpectedCompletionEntry> = [
|
||||
"apply",
|
||||
"call",
|
||||
"bind",
|
||||
"toString",
|
||||
"length",
|
||||
{ name: "arguments", text: "(property) Function.arguments: any" },
|
||||
"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 functionMembersWithPrototype: ReadonlyArray<ExpectedCompletionEntry> = [
|
||||
...functionMembers.slice(0, 4),
|
||||
"prototype",
|
||||
...functionMembers.slice(4),
|
||||
];
|
||||
|
||||
// TODO: Shouldn't propose type keywords in statement position
|
||||
export const statementKeywordsWithTypes: ReadonlyArray<string> = [
|
||||
"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",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"static",
|
||||
"yield",
|
||||
"abstract",
|
||||
"as",
|
||||
"any",
|
||||
"async",
|
||||
"await",
|
||||
"boolean",
|
||||
"constructor",
|
||||
"declare",
|
||||
"get",
|
||||
"infer",
|
||||
"is",
|
||||
"keyof",
|
||||
"module",
|
||||
"namespace",
|
||||
"never",
|
||||
"readonly",
|
||||
"require",
|
||||
"number",
|
||||
"object",
|
||||
"set",
|
||||
"string",
|
||||
"symbol",
|
||||
"type",
|
||||
"unique",
|
||||
"unknown",
|
||||
"from",
|
||||
"global",
|
||||
"of",
|
||||
];
|
||||
|
||||
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",
|
||||
];
|
||||
|
||||
// TODO: many of these are inappropriate to always provide
|
||||
export const globalsInsideFunction = (plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> => [
|
||||
"arguments",
|
||||
...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",
|
||||
];
|
||||
|
||||
// TODO: many of these are inappropriate to always provide
|
||||
export const globalKeywords: ReadonlyArray<string> = [
|
||||
"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",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"static",
|
||||
"yield",
|
||||
"abstract",
|
||||
"as",
|
||||
"any",
|
||||
"async",
|
||||
"await",
|
||||
"boolean",
|
||||
"constructor",
|
||||
"declare",
|
||||
"get",
|
||||
"infer",
|
||||
"is",
|
||||
"keyof",
|
||||
"module",
|
||||
"namespace",
|
||||
"never",
|
||||
"readonly",
|
||||
"require",
|
||||
"number",
|
||||
"object",
|
||||
"set",
|
||||
"string",
|
||||
"symbol",
|
||||
"type",
|
||||
"unique",
|
||||
"unknown",
|
||||
"from",
|
||||
"global",
|
||||
"of",
|
||||
];
|
||||
|
||||
export const insideMethodKeywords: ReadonlyArray<string> = [
|
||||
"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",
|
||||
];
|
||||
|
||||
export const globalKeywordsPlusUndefined: ReadonlyArray<string> = (() => {
|
||||
const i = globalKeywords.indexOf("unique");
|
||||
return [...globalKeywords.slice(0, i), "undefined", ...globalKeywords.slice(i)];
|
||||
})();
|
||||
|
||||
export const globals: ReadonlyArray<string> = [...globalsVars, "undefined", ...globalKeywords];
|
||||
|
||||
export function globalsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
|
||||
return [...globalsVars, ...plus, "undefined", ...globalKeywords];
|
||||
}
|
||||
}
|
||||
|
||||
export interface ReferenceGroup {
|
||||
definition: ReferenceGroupDefinition;
|
||||
@@ -4784,22 +4902,20 @@ namespace FourSlashInterface {
|
||||
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 text: string;
|
||||
readonly documentation: string;
|
||||
readonly kindModifiers?: string;
|
||||
readonly text?: string;
|
||||
readonly documentation?: string;
|
||||
readonly sourceDisplay?: string;
|
||||
readonly tags?: ReadonlyArray<ts.JSDocTagInfo>;
|
||||
};
|
||||
export interface CompletionsAtOptions extends Partial<ts.UserPreferences> {
|
||||
triggerCharacter?: ts.CompletionsTriggerCharacter;
|
||||
isNewIdentifierLocation?: boolean;
|
||||
}
|
||||
|
||||
export interface VerifyCompletionsOptions {
|
||||
readonly marker?: ArrayOrSingle<string | FourSlash.Marker>;
|
||||
readonly isNewIdentifierLocation?: boolean;
|
||||
readonly isNewIdentifierLocation?: boolean; // Always tested
|
||||
readonly isGlobalCompletion?: boolean; // Only tested if set
|
||||
readonly exact?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
readonly includes?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
readonly excludes?: ArrayOrSingle<string | { readonly name: string, readonly source: string }>;
|
||||
readonly excludes?: ArrayOrSingle<string>;
|
||||
readonly preferences?: ts.UserPreferences;
|
||||
readonly triggerCharacter?: ts.CompletionsTriggerCharacter;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user