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:
Родитель
9edddc73ad
Коммит
506f3e26c3
|
@ -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],
|
||||
}],
|
||||
});
|
||||
}
|
Загрузка…
Ссылка в новой задаче