mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-18 07:29:16 -05: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:
@@ -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
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user