mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Fix replacementSpan for class member snippet completion entries (#52231)
This commit is contained in:
@@ -984,8 +984,9 @@ export class TestState {
|
||||
if (actual.insertText !== expected.insertText) {
|
||||
this.raiseError(`At entry ${actual.name}: Completion insert text did not match: ${showTextDiff(expected.insertText || "", actual.insertText || "")}`);
|
||||
}
|
||||
|
||||
const convertedReplacementSpan = expected.replacementSpan && ts.createTextSpanFromRange(expected.replacementSpan);
|
||||
if (convertedReplacementSpan?.length) {
|
||||
if (convertedReplacementSpan) {
|
||||
try {
|
||||
assert.deepEqual(actual.replacementSpan, convertedReplacementSpan);
|
||||
}
|
||||
@@ -993,6 +994,9 @@ export class TestState {
|
||||
this.raiseError(`At entry ${actual.name}: Expected completion replacementSpan to be ${stringify(convertedReplacementSpan)}, got ${stringify(actual.replacementSpan)}`);
|
||||
}
|
||||
}
|
||||
else if (ts.hasProperty(expected, "replacementSpan")) { // Expected `replacementSpan` is explicitly set as `undefined`.
|
||||
assert.equal(actual.replacementSpan, undefined, `At entry ${actual.name}: Expected 'replacementSpan' properties to match`);
|
||||
}
|
||||
|
||||
if (expected.kind !== undefined || expected.kindModifiers !== undefined) {
|
||||
assert.equal(actual.kind, expected.kind, `At entry ${actual.name}: Expected 'kind' for ${actual.name} to match`);
|
||||
|
||||
@@ -927,6 +927,7 @@ function completionInfoFromData(
|
||||
/*replacementToken*/ undefined,
|
||||
contextToken,
|
||||
location,
|
||||
position,
|
||||
sourceFile,
|
||||
host,
|
||||
program,
|
||||
@@ -1316,6 +1317,7 @@ function createCompletionEntry(
|
||||
replacementToken: Node | undefined,
|
||||
contextToken: Node | undefined,
|
||||
location: Node,
|
||||
position: number,
|
||||
sourceFile: SourceFile,
|
||||
host: LanguageServiceHost,
|
||||
program: Program,
|
||||
@@ -1406,7 +1408,8 @@ function createCompletionEntry(
|
||||
completionKind === CompletionKind.MemberLike &&
|
||||
isClassLikeMemberCompletion(symbol, location, sourceFile)) {
|
||||
let importAdder;
|
||||
({ insertText, isSnippet, importAdder, replacementSpan } = getEntryForMemberCompletion(host, program, options, preferences, name, symbol, location, contextToken, formatContext));
|
||||
({ insertText, isSnippet, importAdder, replacementSpan } =
|
||||
getEntryForMemberCompletion(host, program, options, preferences, name, symbol, location, position, contextToken, formatContext));
|
||||
sortText = SortText.ClassMemberSnippets; // sortText has to be lower priority than the sortText for keywords. See #47852.
|
||||
if (importAdder?.hasFixes()) {
|
||||
hasAction = true;
|
||||
@@ -1545,6 +1548,7 @@ function getEntryForMemberCompletion(
|
||||
name: string,
|
||||
symbol: Symbol,
|
||||
location: Node,
|
||||
position: number,
|
||||
contextToken: Node | undefined,
|
||||
formatContext: formatting.FormatContext | undefined,
|
||||
): { insertText: string, isSnippet?: true, importAdder?: codefix.ImportAdder, replacementSpan?: TextSpan } {
|
||||
@@ -1586,7 +1590,7 @@ function getEntryForMemberCompletion(
|
||||
let modifiers = ModifierFlags.None;
|
||||
// Whether the suggested member should be abstract.
|
||||
// e.g. in `abstract class C { abstract | }`, we should offer abstract method signatures at position `|`.
|
||||
const { modifiers: presentModifiers, span: modifiersSpan } = getPresentModifiers(contextToken);
|
||||
const { modifiers: presentModifiers, span: modifiersSpan } = getPresentModifiers(contextToken, sourceFile, position);
|
||||
const isAbstract = !!(presentModifiers & ModifierFlags.Abstract);
|
||||
const completionNodes: Node[] = [];
|
||||
codefix.addNewNodeForMemberSymbol(
|
||||
@@ -1650,8 +1654,13 @@ function getEntryForMemberCompletion(
|
||||
return { insertText, isSnippet, importAdder, replacementSpan };
|
||||
}
|
||||
|
||||
function getPresentModifiers(contextToken: Node | undefined): { modifiers: ModifierFlags, span?: TextSpan } {
|
||||
if (!contextToken) {
|
||||
function getPresentModifiers(
|
||||
contextToken: Node | undefined,
|
||||
sourceFile: SourceFile,
|
||||
position: number): { modifiers: ModifierFlags, span?: TextSpan } {
|
||||
if (!contextToken ||
|
||||
getLineAndCharacterOfPosition(sourceFile, position).line
|
||||
> getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line) {
|
||||
return { modifiers: ModifierFlags.None };
|
||||
}
|
||||
let modifiers = ModifierFlags.None;
|
||||
@@ -2086,6 +2095,7 @@ export function getCompletionEntriesFromSymbols(
|
||||
replacementToken: Node | undefined,
|
||||
contextToken: Node | undefined,
|
||||
location: Node,
|
||||
position: number,
|
||||
sourceFile: SourceFile,
|
||||
host: LanguageServiceHost,
|
||||
program: Program,
|
||||
@@ -2132,6 +2142,7 @@ export function getCompletionEntriesFromSymbols(
|
||||
replacementToken,
|
||||
contextToken,
|
||||
location,
|
||||
position,
|
||||
sourceFile,
|
||||
host,
|
||||
program,
|
||||
@@ -2471,6 +2482,7 @@ function getCompletionEntryCodeActionsAndSourceDisplay(
|
||||
name,
|
||||
symbol,
|
||||
location,
|
||||
position,
|
||||
contextToken,
|
||||
formatContext);
|
||||
if (importAdder) {
|
||||
|
||||
@@ -198,7 +198,7 @@ export function getStringLiteralCompletions(
|
||||
if (isInString(sourceFile, position, contextToken)) {
|
||||
if (!contextToken || !isStringLiteralLike(contextToken)) return undefined;
|
||||
const entries = getStringLiteralCompletionEntries(sourceFile, contextToken, position, program.getTypeChecker(), options, host, preferences);
|
||||
return convertStringLiteralCompletions(entries, contextToken, sourceFile, host, program, log, options, preferences);
|
||||
return convertStringLiteralCompletions(entries, contextToken, sourceFile, host, program, log, options, preferences, position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +211,7 @@ function convertStringLiteralCompletions(
|
||||
log: Log,
|
||||
options: CompilerOptions,
|
||||
preferences: UserPreferences,
|
||||
position: number,
|
||||
): CompletionInfo | undefined {
|
||||
if (completion === undefined) {
|
||||
return undefined;
|
||||
@@ -228,6 +229,7 @@ function convertStringLiteralCompletions(
|
||||
contextToken,
|
||||
contextToken,
|
||||
sourceFile,
|
||||
position,
|
||||
sourceFile,
|
||||
host,
|
||||
program,
|
||||
|
||||
@@ -173,10 +173,6 @@
|
||||
"kindModifiers": "",
|
||||
"sortText": "17",
|
||||
"insertText": "method(): void {\r\n}",
|
||||
"replacementSpan": {
|
||||
"start": 71,
|
||||
"length": 16
|
||||
},
|
||||
"displayParts": [
|
||||
{
|
||||
"text": "(",
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @Filename: a.ts
|
||||
// @newline: LF
|
||||
|
||||
// Issue #52211
|
||||
|
||||
//// interface Interface {
|
||||
//// method(): void;
|
||||
//// }
|
||||
////
|
||||
//// export class Class implements Interface {
|
||||
//// property = "yadda";
|
||||
////
|
||||
//// /**/
|
||||
//// }
|
||||
|
||||
verify.completions({
|
||||
marker: "",
|
||||
isNewIdentifierLocation: true,
|
||||
preferences: {
|
||||
includeCompletionsWithInsertText: true,
|
||||
includeCompletionsWithSnippetText: false,
|
||||
includeCompletionsWithClassMemberSnippets: true,
|
||||
},
|
||||
includes: [
|
||||
{
|
||||
name: "method",
|
||||
sortText: completion.SortText.ClassMemberSnippets,
|
||||
insertText: "method(): void {\n}",
|
||||
replacementSpan: undefined,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -20,7 +20,7 @@
|
||||
//// [|import f/*4*/ =|]
|
||||
|
||||
// @Filename: /index5.ts
|
||||
//// import f/*5*/ from "";
|
||||
//// [|import f/*5*/ from "";|]
|
||||
|
||||
([[0, true], [1, true], [2, false], [3, true], [4, true], [5, true]] as const).forEach(([marker, typeKeywordValid]) => {
|
||||
verify.completions({
|
||||
|
||||
Reference in New Issue
Block a user