Added toggleLineComment, toggleMultilineComment with jsx and tests

This commit is contained in:
Armando Aguirre 2020-02-20 17:00:23 -08:00
parent 3eaa7c65f6
commit 33333e86eb
24 changed files with 631 additions and 9 deletions

View File

@ -812,6 +812,14 @@ namespace ts.server {
return notImplemented();
}
toggleLineComment(): ts.TextChange[] {
throw new Error("Method not implemented.");
}
toggleMultilineComment(): ts.TextChange[] {
throw new Error("Method not implemented.");
}
dispose(): void {
throw new Error("dispose is not available through the server layer.");
}

View File

@ -3657,6 +3657,26 @@ namespace FourSlash {
public configurePlugin(pluginName: string, configuration: any): void {
(<ts.server.SessionClient>this.languageService).configurePlugin(pluginName, configuration);
}
public toggleLineComment(newFileContent: string): void {
const ranges = this.getRanges();
assert(ranges.length);
const changes = this.languageService.toggleLineComment(this.activeFile.fileName, ranges);
this.applyEdits(this.activeFile.fileName, changes);
this.verifyCurrentFileContent(newFileContent);
}
public toggleMultilineComment(newFileContent: string): void {
const ranges = this.getRanges();
assert(ranges.length);
const changes = this.languageService.toggleMultilineComment(this.activeFile.fileName, ranges);
this.applyEdits(this.activeFile.fileName, changes);
this.verifyCurrentFileContent(newFileContent);
}
}
function prefixMessage(message: string | undefined) {

View File

@ -210,6 +210,14 @@ namespace FourSlashInterface {
public refactorAvailable(name: string, actionName?: string) {
this.state.verifyRefactorAvailable(this.negative, name, actionName);
}
public toggleLineComment(newFileContent: string) {
this.state.toggleLineComment(newFileContent);
}
public toggleMultilineComment(newFileContent: string) {
this.state.toggleMultilineComment(newFileContent);
}
}
export class Verify extends VerifyNegatable {

View File

@ -600,6 +600,12 @@ namespace Harness.LanguageService {
clearSourceMapperCache(): never {
return ts.notImplemented();
}
toggleLineComment(fileName: string, textRanges: ts.TextRange[]): ts.TextChange[] {
return unwrapJSONCallResult(this.shim.toggleLineComment(fileName, textRanges));
}
toggleMultilineComment(fileName: string, textRanges: ts.TextRange[]): ts.TextChange[] {
return unwrapJSONCallResult(this.shim.toggleMultilineComment(fileName, textRanges));
}
dispose(): void { this.shim.dispose({}); }
}

View File

@ -136,6 +136,10 @@ namespace ts.server.protocol {
SelectionRange = "selectionRange",
/* @internal */
SelectionRangeFull = "selectionRange-full",
ToggleLineComment = "toggleLineComment",
ToggleLineCommentFull = "toggleLineComment-full",
ToggleMultilineComment = "toggleMultilineComment",
ToggleMultilineCommentFull = "toggleMultilineComment-full",
PrepareCallHierarchy = "prepareCallHierarchy",
ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls",
@ -919,6 +923,18 @@ namespace ts.server.protocol {
end: Location;
}
export interface TextRange {
/**
* Position of the first character.
*/
pos: number;
/**
* Position of the last character.
*/
end: number;
}
/**
* Object found in response messages defining a span of text in a specific source file.
*/
@ -1533,6 +1549,24 @@ namespace ts.server.protocol {
parent?: SelectionRange;
}
export interface ToggleLineCommentRequest extends FileRequest {
command: CommandTypes.ToggleLineComment;
arguments: ToggleLineCommentRequestArgs;
}
export interface ToggleLineCommentRequestArgs extends FileRequestArgs {
textRanges: TextRange[];
}
export interface ToggleMultilineCommentRequest extends FileRequest {
command: CommandTypes.ToggleMultilineComment;
arguments: ToggleMultilineCommentRequestArgs;
}
export interface ToggleMultilineCommentRequestArgs extends FileRequestArgs {
textRanges: TextRange[];
}
/**
* Information found in an "open" request.
*/

View File

@ -2196,6 +2196,22 @@ namespace ts.server {
});
}
private toggleLineComment(args: protocol.ToggleLineCommentRequestArgs, simplifiedResult: boolean) {
const { file, project } = this.getFileAndProject(args);
const result = project.getLanguageService().toggleLineComment(file, args.textRanges);
return simplifiedResult ? [] : result;
}
private toggleMultilineComment(args: protocol.ToggleMultilineCommentRequestArgs, simplifiedResult: boolean) {
const { file, project } = this.getFileAndProject(args);
const result = project.getLanguageService().toggleMultilineComment(file, args.textRanges);
return simplifiedResult ? [] : result;
}
private mapSelectionRange(selectionRange: SelectionRange, scriptInfo: ScriptInfo): protocol.SelectionRange {
const result: protocol.SelectionRange = {
textSpan: toProtocolTextSpan(selectionRange.textSpan, scriptInfo),
@ -2641,6 +2657,18 @@ namespace ts.server {
[CommandNames.ProvideCallHierarchyOutgoingCalls]: (request: protocol.ProvideCallHierarchyOutgoingCallsRequest) => {
return this.requiredResponse(this.provideCallHierarchyOutgoingCalls(request.arguments));
},
[CommandNames.ToggleLineComment]: (request: protocol.ToggleLineCommentRequest) => {
return this.requiredResponse(this.toggleLineComment(request.arguments, /*simplifiedResult*/true));
},
[CommandNames.ToggleLineCommentFull]: (request: protocol.ToggleLineCommentRequest) => {
return this.requiredResponse(this.toggleLineComment(request.arguments, /*simplifiedResult*/false));
},
[CommandNames.ToggleMultilineComment]: (request: protocol.ToggleMultilineCommentRequest) => {
return this.requiredResponse(this.toggleMultilineComment(request.arguments, /*simplifiedResult*/true));
},
[CommandNames.ToggleMultilineComment]: (request: protocol.ToggleMultilineCommentRequest) => {
return this.requiredResponse(this.toggleMultilineComment(request.arguments, /*simplifiedResult*/false));
},
});
public addProtocolHandler(command: string, handler: (request: protocol.Request) => HandlerResponse) {

View File

@ -773,7 +773,7 @@ namespace ts {
if (!hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier)) {
break;
}
// falls through
// falls through
case SyntaxKind.VariableDeclaration:
case SyntaxKind.BindingElement: {
@ -834,7 +834,7 @@ namespace ts {
if (getAssignmentDeclarationKind(node as BinaryExpression) !== AssignmentDeclarationKind.None) {
addDeclaration(node as BinaryExpression);
}
// falls through
// falls through
default:
forEachChild(node, visit);
@ -1977,6 +1977,185 @@ namespace ts {
}
}
function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) {
return {
lineStarts: sourceFile.getLineStarts(),
firstLine: sourceFile.getLineAndCharacterOfPosition(textRange.pos).line,
lastLine: sourceFile.getLineAndCharacterOfPosition(textRange.end).line
}
}
function toggleLineComment(fileName: string, textRanges: TextRange[]): TextChange[] {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
const textChanges: TextChange[] = [];
for (const textRange of textRanges) {
const { lineStarts, firstLine, lastLine } = getLinesForRange(sourceFile, textRange);
let isCommenting = false;
let leftMostPosition = Number.MAX_VALUE;
let lineTextStarts = new Map<number>();
const whiteSpaceRegex = new RegExp(/\S/);
// First check the lines before any text changes.
for (let i = firstLine; i <= lastLine; i++) {
const lineText = sourceFile.text.substring(lineStarts[i], lineStarts[i + 1]); // TODO: Validate the end of line it might go outside of range.
// Find the start of text and the left-most character. No-op on empty lines.
const regExec = whiteSpaceRegex.exec(lineText);
if (regExec) {
leftMostPosition = Math.min(leftMostPosition, regExec.index);
lineTextStarts.set(i.toString(), regExec.index);
// let sourceFilePosition = lineStarts[i] + leftMostPosition;
if (lineText.substr(regExec.index, 3) !== "// ") { // TODO: Validate when it is inside a comment. It can only uncomment if it's inside a comment. // TODO: Check when not finishing on empty space.
isCommenting = true;
}
}
}
for (let i = firstLine; i <= lastLine; i++) {
const lineTextStart = lineTextStarts.get(i.toString());
// If the line is not an empty line; otherwise no-op;
if (lineTextStart !== undefined) {
if (isCommenting) {
textChanges.push({
newText: "// ",
span: {
length: 0,
start: lineStarts[i] + leftMostPosition
}
});
} else {
textChanges.push({
newText: "",
span: {
length: 3,
start: lineStarts[i] + lineTextStart
}
});
}
}
}
}
return textChanges;
}
function toggleMultilineComment(fileName: string, textRanges: TextRange[]): TextChange[] {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
const textChanges: TextChange[] = [];
const { text } = sourceFile;
for (const textRange of textRanges) {
let isCommenting = false;
const positions = [] as number[] as SortedArray<number>;
let pos = textRange.pos;
const isJsx = isInsideJsxTags(sourceFile, pos);
const openMultiline = isJsx ? "{/*" : "/*";
const closeMultiline = isJsx ? "*/}" : "*/";
const openMultilineRegex = isJsx ? "\\{\\/\\*" : "\\/\\*";
const closeMultilineRegex = isJsx ? "\\*\\/\\}" : "\\*\\/";
// Get all comment positions
while (pos <= textRange.end) {
// Start of comment is considered inside comment.
const offset = text.substr(pos, openMultiline.length) === openMultiline ? openMultiline.length : 0;
const commentRange = isInComment(sourceFile, pos + offset);
// If position is in a comment add it to the positions array.
if (commentRange) {
// Include brace positions.
if (isJsx) {
commentRange.pos--;
commentRange.end++;
}
positions.push(commentRange.pos);
if (commentRange.kind === SyntaxKind.MultiLineCommentTrivia) {
positions.push(commentRange.end);
}
pos = commentRange.end + 1;
} else {
isCommenting = true;
const newPos = text.substring(pos, textRange.end).search(`(${openMultilineRegex})|(${closeMultilineRegex})`);
pos = newPos === -1 ? textRange.end + 1 : pos + newPos + closeMultiline.length;
}
}
if (isCommenting) {
if (isInComment(sourceFile, textRange.pos)?.kind !== SyntaxKind.SingleLineCommentTrivia) {
insertSorted(positions, textRange.pos, compareValues);
}
insertSorted(positions, textRange.end, compareValues);
// Insert open comment if the first position is not a comment already.
const firstPos = positions[0];
if (text.substr(firstPos, openMultiline.length) !== openMultiline) {
textChanges.push({
newText: openMultiline,
span: {
length: 0,
start: firstPos
}
});
}
// Insert open and close comment to all positions between first and last. Exclusive.
for (let i = 1; i < positions.length - 1; i++) {
if (text.substr(positions[i] - closeMultiline.length, closeMultiline.length) !== closeMultiline) {
textChanges.push({
newText: closeMultiline,
span: {
length: 0,
start: positions[i]
}
});
}
if (text.substr(positions[i], openMultiline.length) !== openMultiline) {
textChanges.push({
newText: openMultiline,
span: {
length: 0,
start: positions[i]
}
});
}
}
// Insert open comment if the last position is not a comment already.
const lastPos = positions[positions.length - 1];
if (text.substr(lastPos - closeMultiline.length, closeMultiline.length) !== closeMultiline) {
textChanges.push({
newText: closeMultiline,
span: {
length: 0,
start: lastPos
}
});
}
} else {
for (let i = 0; i < positions.length; i++) {
const offset = text.substr(positions[i] - closeMultiline.length, closeMultiline.length) === closeMultiline ? closeMultiline.length : 0;
textChanges.push({
newText: "",
span: {
length: 2,
start: positions[i] - offset
}
});
}
}
}
return textChanges;
}
function isUnclosedTag({ openingElement, closingElement, parent }: JsxElement): boolean {
return !tagNamesAreEquivalent(openingElement.tagName, closingElement.tagName) ||
isJsxElement(parent) && tagNamesAreEquivalent(openingElement.tagName, parent.openingElement.tagName) && isUnclosedTag(parent);
@ -2255,7 +2434,9 @@ namespace ts {
clearSourceMapperCache: () => sourceMapper.clearCache(),
prepareCallHierarchy,
provideCallHierarchyIncomingCalls,
provideCallHierarchyOutgoingCalls
provideCallHierarchyOutgoingCalls,
toggleLineComment,
toggleMultilineComment
};
}
@ -2319,7 +2500,7 @@ namespace ts {
if (node.parent.kind === SyntaxKind.ComputedPropertyName) {
return isObjectLiteralElement(node.parent.parent) ? node.parent.parent : undefined;
}
// falls through
// falls through
case SyntaxKind.Identifier:
return isObjectLiteralElement(node.parent) &&

View File

@ -277,6 +277,9 @@ namespace ts {
getEmitOutput(fileName: string): string;
getEmitOutputObject(fileName: string): EmitOutput;
toggleLineComment(fileName: string, textChanges: ts.TextRange[]): string;
toggleMultilineComment(fileName: string, textChanges: ts.TextRange[]): string;
}
export interface ClassifierShim extends Shim {
@ -1066,6 +1069,20 @@ namespace ts {
() => this.languageService.getEmitOutput(fileName),
this.logPerformance) as EmitOutput;
}
public toggleLineComment(fileName: string, textRanges: ts.TextRange[]): string {
return this.forwardJSONCall(
`toggleLineComment('${fileName}', '${JSON.stringify(textRanges)}')`,
() => this.languageService.toggleLineComment(fileName, textRanges)
);
}
public toggleMultilineComment(fileName: string, textRanges: ts.TextRange[]): string {
return this.forwardJSONCall(
`toggleMultilineComment('${fileName}', '${JSON.stringify(textRanges)}')`,
() => this.languageService.toggleMultilineComment(fileName, textRanges)
);
}
}
function convertClassifications(classifications: Classifications): { spans: string, endOfLineState: EndOfLineState } {

View File

@ -486,6 +486,9 @@ namespace ts {
/* @internal */ getNonBoundSourceFile(fileName: string): SourceFile;
toggleLineComment(fileName: string, textRanges: TextRange[]): TextChange[];
toggleMultilineComment(fileName: string, textRanges: TextRange[]): TextChange[];
dispose(): void;
}

View File

@ -394,7 +394,7 @@ namespace ts {
case SyntaxKind.MethodSignature:
return ScriptElementKind.memberFunctionElement;
case SyntaxKind.PropertyAssignment:
const {initializer} = node as PropertyAssignment;
const { initializer } = node as PropertyAssignment;
return isFunctionLike(initializer) ? ScriptElementKind.memberFunctionElement : ScriptElementKind.memberVariableElement;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
@ -557,7 +557,7 @@ namespace ts {
if (!(<NewExpression>n).arguments) {
return true;
}
// falls through
// falls through
case SyntaxKind.CallExpression:
case SyntaxKind.ParenthesizedExpression:
@ -1320,6 +1320,25 @@ namespace ts {
return false;
}
export function isInsideJsxTags(sourceFile: SourceFile, position: number) {
const token = getTokenAtPosition(sourceFile, position);
switch (token.kind) {
case SyntaxKind.JsxText:
return true;
case SyntaxKind.LessThanToken:
case SyntaxKind.Identifier:
return token.parent.kind === SyntaxKind.JsxText // <div>Hello |</div>
|| token.parent.kind === SyntaxKind.JsxClosingElement // <div>|</div>
|| isJsxOpeningLikeElement(token.parent) && isJsxElement(token.parent.parent) // <div>|<component /></div> or <div><comp|onent /></div>
case SyntaxKind.CloseBraceToken:
case SyntaxKind.OpenBraceToken:
return isJsxExpression(token.parent) && isJsxElement(token.parent.parent); // <div>{|}</div> or <div>|{}</div>
}
return false;
}
export function findPrecedingMatchingToken(token: Node, matchingTokenKind: SyntaxKind, sourceFile: SourceFile) {
const tokenKind = token.kind;
let remainingMatchingTokens = 0;
@ -1346,7 +1365,7 @@ namespace ts {
export function removeOptionality(type: Type, isOptionalExpression: boolean, isOptionalChain: boolean) {
return isOptionalExpression ? type.getNonNullableType() :
isOptionalChain ? type.getNonOptionalType() :
type;
type;
}
export function isPossiblyTypeArgumentPosition(token: Node, sourceFile: SourceFile, checker: TypeChecker): boolean {
@ -1439,7 +1458,7 @@ namespace ts {
break;
case SyntaxKind.EqualsGreaterThanToken:
// falls through
// falls through
case SyntaxKind.Identifier:
case SyntaxKind.StringLiteral:
@ -1447,7 +1466,7 @@ namespace ts {
case SyntaxKind.BigIntLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
// falls through
// falls through
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.ExtendsKeyword:

View File

@ -272,6 +272,8 @@ namespace ts.server {
CommandNames.PrepareCallHierarchy,
CommandNames.ProvideCallHierarchyIncomingCalls,
CommandNames.ProvideCallHierarchyOutgoingCalls,
CommandNames.ToggleLineComment,
CommandNames.ToggleMultilineComment
];
it("should not throw when commands are executed with invalid arguments", () => {

View File

@ -396,6 +396,9 @@ declare namespace FourSlashInterface {
generateTypes(...options: GenerateTypesOptions[]): void;
organizeImports(newContent: string): void;
toggleLineComment(newFileContent: string): void;
toggleBlockComment(newFileContent: string): void;
}
class edit {
backspace(count?: number): void;

View File

@ -0,0 +1,18 @@
// Simple comment and uncomment.
//// let var1[| = 1;
//// let var2 = 2;
//// let var3 |]= 3;
////
//// // let var4[| = 1;
//// // let var5 = 2;
//// // let var6 |]= 3;
verify.toggleLineComment(
`// let var1 = 1;
// let var2 = 2;
// let var3 = 3;
let var4 = 1;
let var5 = 2;
let var6 = 3;`);

View File

@ -0,0 +1,20 @@
// When indentation is different between lines it should get the left most indentation
// and use that for all lines.
// When uncommeting, doesn't matter what indentation the line has.
//// let var1[| = 1;
//// let var2 = 2;
//// let var3 |]= 3;
////
//// // let var4[| = 1;
//// // let var5 = 2;
//// // let var6 |]= 3;
verify.toggleLineComment(
`// let var1 = 1;
// let var2 = 2;
// let var3 = 3;
let var4 = 1;
let var5 = 2;
let var6 = 3;`);

View File

@ -0,0 +1,26 @@
// Comment and uncomment ignores empty lines.
//// let var1[| = 1;
////
//// let var2 = 2;
////
//// let var3 |]= 3;
////
//// // let var4[| = 1;
////
//// // let var5 = 2;
////
//// // let var6 |]= 3;
verify.toggleLineComment(
`// let var1 = 1;
// let var2 = 2;
// let var3 = 3;
let var4 = 1;
let var5 = 2;
let var6 = 3;`);

View File

@ -0,0 +1,18 @@
// If at least one line is uncomment then comment all lines again.
//// // let var1[| = 1;
//// let var2 = 2;
//// // let var3 |]= 3;
////
//// // // let var4[| = 1;
//// // let var5 = 2;
//// // // let var6 |]= 3;
verify.toggleLineComment(
`// // let var1 = 1;
// let var2 = 2;
// // let var3 = 3;
// let var4 = 1;
let var5 = 2;
// let var6 = 3;`);

View File

@ -0,0 +1,22 @@
// Comments inside strings are still considered comments.
//// let var1 = `
//// // some stri[|ng
//// // some other|] string
//// `;
////
//// let var2 = `
//// some stri[|ng
//// some other|] string
//// `;
verify.toggleLineComment(
`let var1 = \`
some string
some other string
\`;
let var2 = \`
// some string
// some other string
\`;`);

View File

@ -0,0 +1,20 @@
// Selection is at the start of jsx it's still considered js.
//// function a() {
//// let foo = "bar";
//// return (
//// [|<div>
//// {foo}|]
//// </div>
//// );
//// }
verify.toggleLineComment(
`function a() {
let foo = "bar";
return (
// <div>
// {foo}
</div>
);
}`);

View File

@ -0,0 +1,26 @@
// Simple block comment and uncomment.
//// let var1[| = 1;
//// let var2 = 2;
//// let var3 |]= 3;
////
//// let var4/* = 1;
//// let var5 [||]= 2;
//// let var6 */= 3;
////
//// [|/*let var7 = 1;
//// let var8 = 2;
//// let var9 = 3;*/|]
verify.toggleBlockComment(
`let var1/* = 1;
let var2 = 2;
let var3 */= 3;
let var4 = 1;
let var5 = 2;
let var6 = 3;
let var7 = 1;
let var8 = 2;
let var9 = 3;`);

View File

@ -0,0 +1,35 @@
// If selection is outside of a block comment then insert comment
// instead of removing.
//// let var1/* = 1;
//// let var2 [|= 2;
//// let var3 */= 3;|]
////
//// [|let var4/* = 1;
//// let var5 |]= 2;
//// let var6 */= 3;
////
//// [|let var7/* = 1;
//// let var8 = 2;
//// let var9 */= 3;|]
////
//// /*let va[|r10 = 1;*/
//// let var11 = 2;
//// /*let var12|] = 3;*/
verify.toggleBlockComment(
`let var1/* = 1;
let var2 *//*= 2;
let var3 *//*= 3;*/
/*let var4*//* = 1;
let var5 *//*= 2;
let var6 */= 3;
/*let var7*//* = 1;
let var8 = 2;
let var9 *//*= 3;*/
/*let va*//*r10 = 1;*//*
let var11 = 2;
*//*let var12*//* = 3;*/`);

View File

@ -0,0 +1,28 @@
/// <reference path="fourslash.ts">
// If range is inside a single line comment, just add the block comment.
//// // let va[|r1 = 1;
//// let var2 = 2;
//// // let var3|] = 3;
////
//// // let va[|r4 = 1;
//// let var5 = 2;
//// /* let var6|] = 3;*/
////
//// /* let va[|r7 = 1;*/
//// let var8 = 2;
//// // let var9|] = 3;
verify.toggleBlockComment(
`/*// let var1 = 1;
let var2 = 2;
// let var3*/ = 3;
/*// let var4 = 1;
let var5 = 2;
*//* let var6*//* = 3;*/
/* let va*//*r7 = 1;*//*
let var8 = 2;
// let var9*/ = 3;`);

View File

@ -0,0 +1,7 @@
// This is an edgecase. The string contains a multiline comment syntax and because it is a string,
// is not actually a comment. When toggling it doesn't get escaped or appended comments.
// The result would be a portion of the selection to be "not commented".
//// /*let s[|omeLongVa*/riable = "Some other /*long th*/in|]g";
verify.toggleMultilineComment(`/*let s*//*omeLongVa*//*riable = "Some other /*long th*/in*/g";`);

View File

@ -0,0 +1,30 @@
// Jsx uses block comments for each line commented.
// Common JSX comment scenarios
//@Filename: file.tsx
//// const a = <div tabIndex="0">[|</div>;|]
//// const b = <div>This is [|valid HTML &amp;|] JSX at the same time.</div>;
//// const c = <MyContainer>
//// [|<MyFirstComponent />
//// <MySecondComponent />|]
//// </MyContainer>;
//// const d = <MyContainer>
//// <MyFirstComp[|onent />
//// <MySecondComponent />|]
//// </MyContainer>;
//// const e = <MyComponent>[|{'foo'}|]</MyComponent>;
verify.toggleBlockComment(
`const a = <div tabIndex="0">{/*</div>;*/}
const b = <div>This is {/*valid HTML &amp;*/} JSX at the same time.</div>;
const c = <MyContainer>
{/*<MyFirstComponent />
<MySecondComponent />*/}
</MyContainer>;
const d = <MyContainer>
<MyFirstComp{/*onent />
<MySecondComponent />*/}
</MyContainer>;
const e = <MyComponent>{/*{'foo'}*/}</MyComponent>;`
);

View File

@ -0,0 +1,43 @@
// Jsx uses multiline comments for each line commented.
// Selection is outside of a block comments inserts block comments instead of removing.
// There's some variations between jsx and js comments depending on the position.
//@Filename: file.tsx
//// const var1 = <div>Tex{/*t1</div>;
//// const var2 = <div>Text2[|</div>;
//// const var3 = <div>Tex*/}t3</div>;|]
////
//// [|const var4 = <div>Tex{/*t4</div>;
//// const var5 = <div|]>Text5</div>;
//// const var6 = <div>Tex*/}t6</div>;
////
//// [|const var7 = <div>Tex{/*t7</div>;
//// const var8 = <div>Text8</div>;
//// const var9 = <div>Tex*/}t9</div>;|]
////
//// const var10 = <div>
//// {/*<div>T[|ext</div>*/}
//// <div>Text</div>
//// {/*<div>Text|]</div>*/}
//// </div>;
verify.toggleMultilineComment(
`const var1 = <div>Tex{/*t1</div>;
const var2 = <div>Text2*/}{/*</div>;
const var3 = <div>Tex*/}{/*t3</div>;*/}
/*const var4 = <div>Tex{*//*t4</div>;
const var5 = <div*//*>Text5</div>;
const var6 = <div>Tex*/}t6</div>;
/*const var7 = <div>Tex{*//*t7</div>;
const var8 = <div>Text8</div>;
const var9 = <div>Tex*//*}t9</div>;*/
const var10 = <div>
{/*<div>T*/}{/*ext</div>*/}{/*
<div>Text</div>
*/}{/*<div>Text*/}{/*</div>*/}
</div>;`
);