From 33333e86ebe6a37d87673bc59ab9311f0a21cd7c Mon Sep 17 00:00:00 2001 From: Armando Aguirre Date: Thu, 20 Feb 2020 17:00:23 -0800 Subject: [PATCH] Added toggleLineComment, toggleMultilineComment with jsx and tests --- src/harness/client.ts | 8 + src/harness/fourslashImpl.ts | 20 ++ src/harness/fourslashInterfaceImpl.ts | 8 + src/harness/harnessLanguageService.ts | 6 + src/server/protocol.ts | 34 ++++ src/server/session.ts | 28 +++ src/services/services.ts | 189 +++++++++++++++++- src/services/shims.ts | 17 ++ src/services/types.ts | 3 + src/services/utilities.ts | 29 ++- src/testRunner/unittests/tsserver/session.ts | 2 + tests/cases/fourslash/fourslash.ts | 3 + tests/cases/fourslash/toggleLineComment1.ts | 18 ++ tests/cases/fourslash/toggleLineComment2.ts | 20 ++ tests/cases/fourslash/toggleLineComment3.ts | 26 +++ tests/cases/fourslash/toggleLineComment4.ts | 18 ++ tests/cases/fourslash/toggleLineComment5.ts | 22 ++ tests/cases/fourslash/toggleLineComment6.ts | 20 ++ .../fourslash/toggleMultilineComment1.ts | 26 +++ .../fourslash/toggleMultilineComment2.ts | 35 ++++ .../fourslash/toggleMultilineComment3.ts | 28 +++ .../fourslash/toggleMultilineComment4.ts | 7 + .../fourslash/toggleMultilineComment5.ts | 30 +++ .../fourslash/toggleMultilineComment6.ts | 43 ++++ 24 files changed, 631 insertions(+), 9 deletions(-) create mode 100644 tests/cases/fourslash/toggleLineComment1.ts create mode 100644 tests/cases/fourslash/toggleLineComment2.ts create mode 100644 tests/cases/fourslash/toggleLineComment3.ts create mode 100644 tests/cases/fourslash/toggleLineComment4.ts create mode 100644 tests/cases/fourslash/toggleLineComment5.ts create mode 100644 tests/cases/fourslash/toggleLineComment6.ts create mode 100644 tests/cases/fourslash/toggleMultilineComment1.ts create mode 100644 tests/cases/fourslash/toggleMultilineComment2.ts create mode 100644 tests/cases/fourslash/toggleMultilineComment3.ts create mode 100644 tests/cases/fourslash/toggleMultilineComment4.ts create mode 100644 tests/cases/fourslash/toggleMultilineComment5.ts create mode 100644 tests/cases/fourslash/toggleMultilineComment6.ts diff --git a/src/harness/client.ts b/src/harness/client.ts index 83e85cbc9a3..2609ecc8485 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -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."); } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index dd4135ca40c..694208856e2 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3657,6 +3657,26 @@ namespace FourSlash { public configurePlugin(pluginName: string, configuration: any): void { (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) { diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index f4905c00b84..76548debdbf 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -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 { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index fbaf9ba545d..43d53d71b26 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -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({}); } } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index b6cc2e4d8fd..bf8cff942c9 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -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. */ diff --git a/src/server/session.ts b/src/server/session.ts index 3f0321074a9..386c737e49a 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -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) { diff --git a/src/services/services.ts b/src/services/services.ts index 28cf22ec473..d87d638bf10 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -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(); + 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; + + 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) && diff --git a/src/services/shims.ts b/src/services/shims.ts index 6cecfeaa674..1e1669be605 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -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 } { diff --git a/src/services/types.ts b/src/services/types.ts index fd8ade8f8e6..39644aa9f0a 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -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; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index b1799167c43..ed20ed7365b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -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 (!(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 //
Hello |
+ || token.parent.kind === SyntaxKind.JsxClosingElement //
|
+ || isJsxOpeningLikeElement(token.parent) && isJsxElement(token.parent.parent) //
|
or
+ case SyntaxKind.CloseBraceToken: + case SyntaxKind.OpenBraceToken: + return isJsxExpression(token.parent) && isJsxElement(token.parent.parent); //
{|}
or
|{}
+ } + + 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: diff --git a/src/testRunner/unittests/tsserver/session.ts b/src/testRunner/unittests/tsserver/session.ts index b41df99f4a2..b0f55affa0e 100644 --- a/src/testRunner/unittests/tsserver/session.ts +++ b/src/testRunner/unittests/tsserver/session.ts @@ -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", () => { diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index d7d4935118d..e61154f2fd3 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -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; diff --git a/tests/cases/fourslash/toggleLineComment1.ts b/tests/cases/fourslash/toggleLineComment1.ts new file mode 100644 index 00000000000..8e634c84d01 --- /dev/null +++ b/tests/cases/fourslash/toggleLineComment1.ts @@ -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;`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleLineComment2.ts b/tests/cases/fourslash/toggleLineComment2.ts new file mode 100644 index 00000000000..ead331a2893 --- /dev/null +++ b/tests/cases/fourslash/toggleLineComment2.ts @@ -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;`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleLineComment3.ts b/tests/cases/fourslash/toggleLineComment3.ts new file mode 100644 index 00000000000..e498bb4ea00 --- /dev/null +++ b/tests/cases/fourslash/toggleLineComment3.ts @@ -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;`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleLineComment4.ts b/tests/cases/fourslash/toggleLineComment4.ts new file mode 100644 index 00000000000..72ebd7b5e07 --- /dev/null +++ b/tests/cases/fourslash/toggleLineComment4.ts @@ -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;`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleLineComment5.ts b/tests/cases/fourslash/toggleLineComment5.ts new file mode 100644 index 00000000000..c5e20dd27b5 --- /dev/null +++ b/tests/cases/fourslash/toggleLineComment5.ts @@ -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 +\`;`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleLineComment6.ts b/tests/cases/fourslash/toggleLineComment6.ts new file mode 100644 index 00000000000..a3b3d9e4a64 --- /dev/null +++ b/tests/cases/fourslash/toggleLineComment6.ts @@ -0,0 +1,20 @@ +// Selection is at the start of jsx it's still considered js. + +//// function a() { +//// let foo = "bar"; +//// return ( +//// [|
+//// {foo}|] +////
+//// ); +//// } + +verify.toggleLineComment( + `function a() { + let foo = "bar"; + return ( + //
+ // {foo} +
+ ); +}`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleMultilineComment1.ts b/tests/cases/fourslash/toggleMultilineComment1.ts new file mode 100644 index 00000000000..cfc0fe87aff --- /dev/null +++ b/tests/cases/fourslash/toggleMultilineComment1.ts @@ -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;`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleMultilineComment2.ts b/tests/cases/fourslash/toggleMultilineComment2.ts new file mode 100644 index 00000000000..62dc1f45e62 --- /dev/null +++ b/tests/cases/fourslash/toggleMultilineComment2.ts @@ -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;*/`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleMultilineComment3.ts b/tests/cases/fourslash/toggleMultilineComment3.ts new file mode 100644 index 00000000000..fab4263b3c3 --- /dev/null +++ b/tests/cases/fourslash/toggleMultilineComment3.ts @@ -0,0 +1,28 @@ +/// + +// 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;`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleMultilineComment4.ts b/tests/cases/fourslash/toggleMultilineComment4.ts new file mode 100644 index 00000000000..1764c5a08e0 --- /dev/null +++ b/tests/cases/fourslash/toggleMultilineComment4.ts @@ -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";`); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleMultilineComment5.ts b/tests/cases/fourslash/toggleMultilineComment5.ts new file mode 100644 index 00000000000..e806ef446b0 --- /dev/null +++ b/tests/cases/fourslash/toggleMultilineComment5.ts @@ -0,0 +1,30 @@ +// Jsx uses block comments for each line commented. + +// Common JSX comment scenarios + +//@Filename: file.tsx +//// const a =
[|
;|] +//// const b =
This is [|valid HTML &|] JSX at the same time.
; +//// const c = +//// [| +//// |] +//// ; +//// const d = +//// +//// |] +//// ; +//// const e = [|{'foo'}|]; + +verify.toggleBlockComment( + `const a =
{/*
;*/} +const b =
This is {/*valid HTML &*/} JSX at the same time.
; +const c = + {/* + */} +; +const d = + + */} +; +const e = {/*{'foo'}*/};` +); \ No newline at end of file diff --git a/tests/cases/fourslash/toggleMultilineComment6.ts b/tests/cases/fourslash/toggleMultilineComment6.ts new file mode 100644 index 00000000000..882fbbffb03 --- /dev/null +++ b/tests/cases/fourslash/toggleMultilineComment6.ts @@ -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 =
Tex{/*t1
; +//// const var2 =
Text2[|
; +//// const var3 =
Tex*/}t3
;|] +//// +//// [|const var4 =
Tex{/*t4
; +//// const var5 = Text5; +//// const var6 =
Tex*/}t6
; +//// +//// [|const var7 =
Tex{/*t7
; +//// const var8 =
Text8
; +//// const var9 =
Tex*/}t9
;|] +//// +//// const var10 =
+//// {/*
T[|ext
*/} +////
Text
+//// {/*
Text|]
*/} +////
; + +verify.toggleMultilineComment( + `const var1 =
Tex{/*t1
; +const var2 =
Text2*/}{/*
; +const var3 =
Tex*/}{/*t3
;*/} + +/*const var4 =
Tex{*//*t4
; +const var5 = Text5; +const var6 =
Tex*/}t6
; + +/*const var7 =
Tex{*//*t7
; +const var8 =
Text8
; +const var9 =
Tex*//*}t9
;*/ + +const var10 =
+ {/*
T*/}{/*ext
*/}{/* +
Text
+ */}{/*
Text*/}{/*
*/} +
;` +); \ No newline at end of file