mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-12 12:57:11 -06:00
Add optionalReplacementSpan to completions response (#40347)
* Add optionalReplacementRange to completions response * Get the name right * Fix unit tests * Fix comment typo * Fix comment typo * Baseline
This commit is contained in:
parent
8384018e68
commit
f6f2d36ee3
@ -830,6 +830,13 @@ namespace FourSlash {
|
||||
this.raiseError(`Expected 'isGlobalCompletion to be ${options.isGlobalCompletion}, got ${actualCompletions.isGlobalCompletion}`);
|
||||
}
|
||||
|
||||
if (ts.hasProperty(options, "optionalReplacementSpan")) {
|
||||
assert.deepEqual(
|
||||
actualCompletions.optionalReplacementSpan && actualCompletions.optionalReplacementSpan,
|
||||
options.optionalReplacementSpan && ts.createTextSpanFromRange(options.optionalReplacementSpan),
|
||||
"Expected 'optionalReplacementSpan' properties to match");
|
||||
}
|
||||
|
||||
const nameToEntries = new ts.Map<string, ts.CompletionEntry[]>();
|
||||
for (const entry of actualCompletions.entries) {
|
||||
const entries = nameToEntries.get(entry.name);
|
||||
|
||||
@ -1529,6 +1529,7 @@ namespace FourSlashInterface {
|
||||
readonly marker?: ArrayOrSingle<string | FourSlash.Marker>;
|
||||
readonly isNewIdentifierLocation?: boolean; // Always tested
|
||||
readonly isGlobalCompletion?: boolean; // Only tested if set
|
||||
readonly optionalReplacementSpan?: FourSlash.Range; // Only tested if set
|
||||
readonly exact?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
readonly includes?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
readonly excludes?: ArrayOrSingle<string>;
|
||||
|
||||
@ -2256,6 +2256,12 @@ namespace ts.server.protocol {
|
||||
readonly isGlobalCompletion: boolean;
|
||||
readonly isMemberCompletion: boolean;
|
||||
readonly isNewIdentifierLocation: boolean;
|
||||
/**
|
||||
* In the absence of `CompletionEntry["replacementSpan"]`, the editor may choose whether to use
|
||||
* this span or its default one. If `CompletionEntry["replacementSpan"]` is defined, that span
|
||||
* must be used to commit that completion entry.
|
||||
*/
|
||||
readonly optionalReplacementSpan?: TextSpan;
|
||||
readonly entries: readonly CompletionEntry[];
|
||||
}
|
||||
|
||||
|
||||
@ -1783,6 +1783,7 @@ namespace ts.server {
|
||||
|
||||
const res: protocol.CompletionInfo = {
|
||||
...completions,
|
||||
optionalReplacementSpan: completions.optionalReplacementSpan && toProtocolTextSpan(completions.optionalReplacementSpan, scriptInfo),
|
||||
entries,
|
||||
};
|
||||
return res;
|
||||
|
||||
@ -209,6 +209,11 @@ namespace ts.Completions {
|
||||
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
|
||||
}
|
||||
|
||||
function getOptionalReplacementSpan(location: Node | undefined) {
|
||||
// StringLiteralLike locations are handled separately in stringCompletions.ts
|
||||
return location?.kind === SyntaxKind.Identifier ? createTextSpanFromNode(location) : undefined;
|
||||
}
|
||||
|
||||
function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, preferences: UserPreferences): CompletionInfo | undefined {
|
||||
const {
|
||||
symbols,
|
||||
@ -241,7 +246,7 @@ namespace ts.Completions {
|
||||
kindModifiers: undefined,
|
||||
sortText: SortText.LocationPriority,
|
||||
};
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: false, entries: [entry] };
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: false, optionalReplacementSpan: getOptionalReplacementSpan(location), entries: [entry] };
|
||||
}
|
||||
|
||||
const entries: CompletionEntry[] = [];
|
||||
@ -305,7 +310,13 @@ namespace ts.Completions {
|
||||
entries.push(createCompletionEntryForLiteral(literal, preferences));
|
||||
}
|
||||
|
||||
return { isGlobalCompletion: isInSnippetScope, isMemberCompletion: isMemberCompletionKind(completionKind), isNewIdentifierLocation, entries };
|
||||
return {
|
||||
isGlobalCompletion: isInSnippetScope,
|
||||
isMemberCompletion: isMemberCompletionKind(completionKind),
|
||||
isNewIdentifierLocation,
|
||||
optionalReplacementSpan: getOptionalReplacementSpan(location),
|
||||
entries
|
||||
};
|
||||
}
|
||||
|
||||
function isUncheckedFile(sourceFile: SourceFile, compilerOptions: CompilerOptions): boolean {
|
||||
|
||||
@ -12,10 +12,12 @@ namespace ts.Completions.StringCompletions {
|
||||
}
|
||||
}
|
||||
|
||||
function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, contextToken: Node, sourceFile: SourceFile, checker: TypeChecker, log: Log, preferences: UserPreferences): CompletionInfo | undefined {
|
||||
function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, contextToken: StringLiteralLike, sourceFile: SourceFile, checker: TypeChecker, log: Log, preferences: UserPreferences): CompletionInfo | undefined {
|
||||
if (completion === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const optionalReplacementSpan = createTextSpanFromStringLiteralLikeContent(contextToken);
|
||||
switch (completion.kind) {
|
||||
case StringLiteralCompletionKind.Paths:
|
||||
return convertPathCompletions(completion.paths);
|
||||
@ -33,7 +35,7 @@ namespace ts.Completions.StringCompletions {
|
||||
CompletionKind.String,
|
||||
preferences
|
||||
); // Target will not be used, so arbitrary
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: completion.hasIndexSignature, entries };
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: completion.hasIndexSignature, optionalReplacementSpan, entries };
|
||||
}
|
||||
case StringLiteralCompletionKind.Types: {
|
||||
const entries = completion.types.map(type => ({
|
||||
@ -43,7 +45,7 @@ namespace ts.Completions.StringCompletions {
|
||||
sortText: "0",
|
||||
replacementSpan: getReplacementSpanForContextToken(contextToken)
|
||||
}));
|
||||
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: completion.isNewIdentifier, entries };
|
||||
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: completion.isNewIdentifier, optionalReplacementSpan, entries };
|
||||
}
|
||||
default:
|
||||
return Debug.assertNever(completion);
|
||||
|
||||
@ -1087,6 +1087,12 @@ namespace ts {
|
||||
/** Not true for all global completions. This will be true if the enclosing scope matches a few syntax kinds. See `isSnippetScope`. */
|
||||
isGlobalCompletion: boolean;
|
||||
isMemberCompletion: boolean;
|
||||
/**
|
||||
* In the absence of `CompletionEntry["replacementSpan"], the editor may choose whether to use
|
||||
* this span or its default one. If `CompletionEntry["replacementSpan"]` is defined, that span
|
||||
* must be used to commit that completion entry.
|
||||
*/
|
||||
optionalReplacementSpan?: TextSpan;
|
||||
|
||||
/**
|
||||
* true when the current location also allows for a new identifier
|
||||
|
||||
@ -44,6 +44,7 @@ namespace ts.projectSystem {
|
||||
isGlobalCompletion: true,
|
||||
isMemberCompletion: false,
|
||||
isNewIdentifierLocation: false,
|
||||
optionalReplacementSpan: { start: { line: 1, offset: 1 }, end: { line: 1, offset: 4 } },
|
||||
entries: [entry],
|
||||
});
|
||||
|
||||
|
||||
@ -80,6 +80,10 @@ namespace ts.projectSystem {
|
||||
isGlobalCompletion: false,
|
||||
isMemberCompletion: true,
|
||||
isNewIdentifierLocation: false,
|
||||
optionalReplacementSpan: {
|
||||
start: { line: 1, offset: aTs.content.indexOf("prop;") + 1 },
|
||||
end: { line: 1, offset: aTs.content.indexOf("prop;") + 1 + "prop".length }
|
||||
},
|
||||
entries: expectedCompletionEntries
|
||||
});
|
||||
});
|
||||
|
||||
@ -5964,6 +5964,12 @@ declare namespace ts {
|
||||
/** Not true for all global completions. This will be true if the enclosing scope matches a few syntax kinds. See `isSnippetScope`. */
|
||||
isGlobalCompletion: boolean;
|
||||
isMemberCompletion: boolean;
|
||||
/**
|
||||
* In the absence of `CompletionEntry["replacementSpan"], the editor may choose whether to use
|
||||
* this span or its default one. If `CompletionEntry["replacementSpan"]` is defined, that span
|
||||
* must be used to commit that completion entry.
|
||||
*/
|
||||
optionalReplacementSpan?: TextSpan;
|
||||
/**
|
||||
* true when the current location also allows for a new identifier
|
||||
*/
|
||||
@ -8102,6 +8108,12 @@ declare namespace ts.server.protocol {
|
||||
readonly isGlobalCompletion: boolean;
|
||||
readonly isMemberCompletion: boolean;
|
||||
readonly isNewIdentifierLocation: boolean;
|
||||
/**
|
||||
* In the absence of `CompletionEntry["replacementSpan"]`, the editor may choose whether to use
|
||||
* this span or its default one. If `CompletionEntry["replacementSpan"]` is defined, that span
|
||||
* must be used to commit that completion entry.
|
||||
*/
|
||||
readonly optionalReplacementSpan?: TextSpan;
|
||||
readonly entries: readonly CompletionEntry[];
|
||||
}
|
||||
interface CompletionDetailsResponse extends Response {
|
||||
|
||||
@ -5964,6 +5964,12 @@ declare namespace ts {
|
||||
/** Not true for all global completions. This will be true if the enclosing scope matches a few syntax kinds. See `isSnippetScope`. */
|
||||
isGlobalCompletion: boolean;
|
||||
isMemberCompletion: boolean;
|
||||
/**
|
||||
* In the absence of `CompletionEntry["replacementSpan"], the editor may choose whether to use
|
||||
* this span or its default one. If `CompletionEntry["replacementSpan"]` is defined, that span
|
||||
* must be used to commit that completion entry.
|
||||
*/
|
||||
optionalReplacementSpan?: TextSpan;
|
||||
/**
|
||||
* true when the current location also allows for a new identifier
|
||||
*/
|
||||
|
||||
13
tests/cases/fourslash/completionsOptionalReplacementSpan1.ts
Normal file
13
tests/cases/fourslash/completionsOptionalReplacementSpan1.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @lib: dom
|
||||
|
||||
//// console.[|cl/*0*/ockwork|];
|
||||
//// type T = Array["[|toS/*1*/paghetti|]"];
|
||||
|
||||
test.ranges().forEach((range, marker) => {
|
||||
verify.completions({
|
||||
marker: `${marker}`,
|
||||
optionalReplacementSpan: range,
|
||||
});
|
||||
});
|
||||
@ -610,6 +610,7 @@ declare namespace FourSlashInterface {
|
||||
readonly marker?: ArrayOrSingle<string | Marker>;
|
||||
readonly isNewIdentifierLocation?: boolean;
|
||||
readonly isGlobalCompletion?: boolean;
|
||||
readonly optionalReplacementSpan?: Range;
|
||||
readonly exact?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
readonly includes?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
readonly excludes?: ArrayOrSingle<string>;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user