fix58438: do not delete comments/code following unterminated string completion (#58742)

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
This commit is contained in:
Isabel Duan 2024-06-03 21:10:32 -07:00 коммит произвёл GitHub
Родитель 9edddc73ad
Коммит 506f3e26c3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
5 изменённых файлов: 86 добавлений и 14 удалений

Просмотреть файл

@ -1661,7 +1661,7 @@ function createCompletionEntry(
): CompletionEntry | undefined {
let insertText: string | undefined;
let filterText: string | undefined;
let replacementSpan = getReplacementSpanForContextToken(replacementToken);
let replacementSpan = getReplacementSpanForContextToken(replacementToken, position);
let data: CompletionEntryData | undefined;
let isSnippet: true | undefined;
let source = getSourceFromOrigin(origin);

Просмотреть файл

@ -223,7 +223,7 @@ function convertStringLiteralCompletions(
return undefined;
}
const optionalReplacementSpan = createTextSpanFromStringLiteralLikeContent(contextToken);
const optionalReplacementSpan = createTextSpanFromStringLiteralLikeContent(contextToken, position);
switch (completion.kind) {
case StringLiteralCompletionKind.Paths:
return convertPathCompletions(completion.paths);
@ -270,7 +270,7 @@ function convertStringLiteralCompletions(
kindModifiers: ScriptElementKindModifier.none,
kind: ScriptElementKind.string,
sortText: SortText.LocationPriority,
replacementSpan: getReplacementSpanForContextToken(contextToken),
replacementSpan: getReplacementSpanForContextToken(contextToken, position),
}));
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: completion.isNewIdentifier, optionalReplacementSpan, entries };
}

Просмотреть файл

@ -2317,13 +2317,13 @@ function isInReferenceCommentWorker(sourceFile: SourceFile, position: number, sh
}
/** @internal */
export function getReplacementSpanForContextToken(contextToken: Node | undefined) {
export function getReplacementSpanForContextToken(contextToken: Node | undefined, position: number) {
if (!contextToken) return undefined;
switch (contextToken.kind) {
case SyntaxKind.StringLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
return createTextSpanFromStringLiteralLikeContent(contextToken as StringLiteralLike);
return createTextSpanFromStringLiteralLikeContent(contextToken as StringLiteralLike, position);
default:
return createTextSpanFromNode(contextToken);
}
@ -2335,12 +2335,12 @@ export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile, endN
}
/** @internal */
export function createTextSpanFromStringLiteralLikeContent(node: StringLiteralLike) {
export function createTextSpanFromStringLiteralLikeContent(node: StringLiteralLike, position: number) {
let replacementEnd = node.getEnd() - 1;
if (node.isUnterminated) {
// we return no replacement range only if unterminated string is empty
if (node.getStart() === replacementEnd) return undefined;
replacementEnd = node.getEnd();
replacementEnd = Math.min(position, node.getEnd());
}
return createTextSpanFromBounds(node.getStart() + 1, replacementEnd);
}

Просмотреть файл

@ -18,15 +18,15 @@
// @Filename: file4.ts
//// const a = { "foo.bar": 1 }
//// a["[|foo./*4*/b|]
//// a["[|foo./*4*/|]b
// @Filename: file5.ts
//// const a = { "foo.bar": 1 }
//// a['[|foo./*5*/b|]
//// a['[|foo./*5*/|]b
// @Filename: file6.ts
//// const a = { "foo.bar": 1 }
//// a[`[|foo./*6*/b|]
//// a[`[|foo./*6*/|]b
// @Filename: file7.ts
//// const a = { "foo.bar": 1 }
@ -44,19 +44,39 @@
//// const a = { "foo.bar": 1 }
//// a["[|foo/*10*/|]"
// @Filename: file11.ts
//// const a = { "foo.bar": 1 }
//// a["[|f/*11*/|]oo.b
// @Filename: file12.ts
//// const a = { "foo.bar": 1 }
//// a["[|f/*12*/oo.b|]"
// @Filename: file13.ts
//// const a = { "foo.bar": 1 }
//// a["[|f/*13*/oo.b|]" // some comment!!
// @Filename: file14.ts
//// const a = { "foo.bar": 1 }
//// a["[|f/*14*/|]oo.b // some comment!!
// @Filename: file15.ts
//// const a = { "foo.bar": 1 }
//// a[`[|foo.b/*15*/|]
// @Filename: empty1.ts
//// const a = { "foo.bar": 1 }
//// a[`/*11*/
//// a[`/*a*/
// @Filename: empty2.ts
//// const a = { "foo.bar": 1 }
//// a["/*12*/
//// a["/*b*/
// @Filename: empty3.ts
//// const a = { "foo.bar": 1 }
//// a['/*13*/
//// a['/*c*/
// tests 11-13 should return no replacementSpan
// tests a,b,c should return no replacementSpan
const markers = test.markers();
const ranges = test.ranges();

Просмотреть файл

@ -0,0 +1,52 @@
///<reference path="fourslash.ts" />
// @Filename: test0.ts
//// type Test = 'test';
//// const test0: Test = `[|t/*0*/|]; // this comment should not be deleted
// @Filename: test1.ts
//// type Test = 'test';
//// const test1: Test = '[|t/*1*/|]; // this comment should not be deleted
// @Filename: test2.ts
//// type Test = 'test';
//// const test2: Test = "[|t/*2*/|] other stuff; // this comment should not be deleted
// @Filename: test3.ts
//// type Test = 'test';
//// const test3: Test = "[|t/*3*/|]es // this comment should not be deleted
// @Filename: test4.ts
//// type Test = 'test';
//// const test4: Test = "[|tes/*4*/|] // this comment should not be deleted
// @Filename: file5.ts
//// type Test = 'test';
//// const test5: {prop0: Test, prop1: number, prop2: number, } = { prop0: '[|t/*5*/|] ,prop1: 5, prop2: 2 };
// @Filename: file6.ts
//// type Test = 'test';
//// const test6: {prop0: Test, prop1: number, prop2: number, } = { prop0: '[|t/*6*/|]es ,prop1: 5, prop2: 2 };
// @Filename: test7.ts
//// type Test = 'test asdf';
//// const test7: Test = `[|test as/*7*/|]; // this comment should not be deleted
// @Filename: test8.ts
//// type Test = 'test asdf';
//// const test8: Test = `[|test/*8*/|] as; // this comment should not be deleted
const markers = test.markers();
const ranges = test.ranges();
for (let i = 0; i < markers.length; i++) {
verify.completions({
marker: markers[i],
includes: [{
"name": "test",
"kind": "string",
"kindModifiers": "",
"replacementSpan": ranges[i],
}],
});
}