From 381dd8427a5356d8b394042e3683a9e843ce906c Mon Sep 17 00:00:00 2001 From: Armando Aguirre Date: Thu, 27 Feb 2020 12:57:51 -0800 Subject: [PATCH] Removed TextRange and added FileRangeRequestArgs --- src/harness/fourslashImpl.ts | 36 ++-- src/harness/harnessLanguageService.ts | 8 +- src/server/protocol.ts | 24 +-- src/server/session.ts | 23 ++- src/services/services.ts | 264 +++++++++++++------------- src/services/shims.ts | 16 +- src/services/types.ts | 4 +- 7 files changed, 180 insertions(+), 195 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 694208856e2..d18f6b84b94 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3190,7 +3190,7 @@ namespace FourSlash { this.raiseError( `Expected to find a fix with the name '${fixName}', but none exists.` + - availableFixes.length + availableFixes.length ? ` Available fixes: ${availableFixes.map(fix => `${fix.fixName} (${fix.fixId ? "with" : "without"} fix-all)`).join(", ")}` : "" ); @@ -3429,13 +3429,13 @@ namespace FourSlash { const incomingCalls = direction === CallHierarchyItemDirection.Outgoing ? { result: "skip" } as const : - alreadySeen ? { result: "seen" } as const : - { result: "show", values: this.languageService.provideCallHierarchyIncomingCalls(callHierarchyItem.file, callHierarchyItem.selectionSpan.start) } as const; + alreadySeen ? { result: "seen" } as const : + { result: "show", values: this.languageService.provideCallHierarchyIncomingCalls(callHierarchyItem.file, callHierarchyItem.selectionSpan.start) } as const; const outgoingCalls = direction === CallHierarchyItemDirection.Incoming ? { result: "skip" } as const : - alreadySeen ? { result: "seen" } as const : - { result: "show", values: this.languageService.provideCallHierarchyOutgoingCalls(callHierarchyItem.file, callHierarchyItem.selectionSpan.start) } as const; + alreadySeen ? { result: "seen" } as const : + { result: "show", values: this.languageService.provideCallHierarchyOutgoingCalls(callHierarchyItem.file, callHierarchyItem.selectionSpan.start) } as const; let text = ""; text += `${prefix}╭ name: ${callHierarchyItem.name}\n`; @@ -3446,7 +3446,7 @@ namespace FourSlash { text += `${prefix}├ selectionSpan:\n`; text += this.formatCallHierarchyItemSpan(file, callHierarchyItem.selectionSpan, `${prefix}│ `, incomingCalls.result !== "skip" || outgoingCalls.result !== "skip" ? `${prefix}│ ` : - `${trailingPrefix}╰ `); + `${trailingPrefix}╰ `); if (incomingCalls.result === "seen") { if (outgoingCalls.result === "skip") { @@ -3475,8 +3475,8 @@ namespace FourSlash { text += `${prefix}│ ├ fromSpans:\n`; text += this.formatCallHierarchyItemSpans(file, incomingCall.fromSpans, `${prefix}│ │ `, i < incomingCalls.values.length - 1 ? `${prefix}│ ╰ ` : - outgoingCalls.result !== "skip" ? `${prefix}│ ╰ ` : - `${trailingPrefix}╰ ╰ `); + outgoingCalls.result !== "skip" ? `${prefix}│ ╰ ` : + `${trailingPrefix}╰ ╰ `); } } } @@ -3497,7 +3497,7 @@ namespace FourSlash { text += `${prefix}│ ├ fromSpans:\n`; text += this.formatCallHierarchyItemSpans(file, outgoingCall.fromSpans, `${prefix}│ │ `, i < outgoingCalls.values.length - 1 ? `${prefix}│ ╰ ` : - `${trailingPrefix}╰ ╰ `); + `${trailingPrefix}╰ ╰ `); } } } @@ -3659,20 +3659,22 @@ namespace FourSlash { } public toggleLineComment(newFileContent: string): void { - const ranges = this.getRanges(); - assert(ranges.length); - const changes = this.languageService.toggleLineComment(this.activeFile.fileName, ranges); - + let changes: ts.TextChange[] = []; + for (let range of this.getRanges()) { + changes.push.apply(changes, this.languageService.toggleLineComment(this.activeFile.fileName, range)); + } + 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); - + let changes: ts.TextChange[] = []; + for (let range of this.getRanges()) { + changes.push.apply(changes, this.languageService.toggleMultilineComment(this.activeFile.fileName, range)); + } + this.applyEdits(this.activeFile.fileName, changes); this.verifyCurrentFileContent(newFileContent); diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 43d53d71b26..06c85842e21 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -600,11 +600,11 @@ namespace Harness.LanguageService { clearSourceMapperCache(): never { return ts.notImplemented(); } - toggleLineComment(fileName: string, textRanges: ts.TextRange[]): ts.TextChange[] { - return unwrapJSONCallResult(this.shim.toggleLineComment(fileName, textRanges)); + toggleLineComment(fileName: string, textRange: ts.TextRange): ts.TextChange[] { + return unwrapJSONCallResult(this.shim.toggleLineComment(fileName, textRange)); } - toggleMultilineComment(fileName: string, textRanges: ts.TextRange[]): ts.TextChange[] { - return unwrapJSONCallResult(this.shim.toggleMultilineComment(fileName, textRanges)); + toggleMultilineComment(fileName: string, textRange: ts.TextRange): ts.TextChange[] { + return unwrapJSONCallResult(this.shim.toggleMultilineComment(fileName, textRange)); } dispose(): void { this.shim.dispose({}); } } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index bf8cff942c9..c459222fff9 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -923,18 +923,6 @@ 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. */ @@ -1551,20 +1539,12 @@ namespace ts.server.protocol { export interface ToggleLineCommentRequest extends FileRequest { command: CommandTypes.ToggleLineComment; - arguments: ToggleLineCommentRequestArgs; - } - - export interface ToggleLineCommentRequestArgs extends FileRequestArgs { - textRanges: TextRange[]; + arguments: FileRangeRequestArgs; } export interface ToggleMultilineCommentRequest extends FileRequest { command: CommandTypes.ToggleMultilineComment; - arguments: ToggleMultilineCommentRequestArgs; - } - - export interface ToggleMultilineCommentRequestArgs extends FileRequestArgs { - textRanges: TextRange[]; + arguments: FileRangeRequestArgs; } /** diff --git a/src/server/session.ts b/src/server/session.ts index d94f0df7f5a..7553997b760 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1957,8 +1957,7 @@ namespace ts.server { position = getPosition(args); } else { - const { startPosition, endPosition } = this.getStartAndEndPosition(args, scriptInfo); - textRange = { pos: startPosition, end: endPosition }; + textRange = this.getRange(args, scriptInfo); } return Debug.checkDefined(position === undefined ? textRange : position); @@ -1967,6 +1966,12 @@ namespace ts.server { } } + private getRange(args: protocol.FileRangeRequestArgs, scriptInfo: ScriptInfo): TextRange { + const { startPosition, endPosition } = this.getStartAndEndPosition(args, scriptInfo); + + return { pos: startPosition, end: endPosition }; + } + private getApplicableRefactors(args: protocol.GetApplicableRefactorsRequestArgs): protocol.ApplicableRefactorInfo[] { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file)!; @@ -2196,10 +2201,12 @@ namespace ts.server { }); } - private toggleLineComment(args: protocol.ToggleLineCommentRequestArgs, simplifiedResult: boolean): TextChange[] | protocol.CodeEdit[] { + private toggleLineComment(args: protocol.FileRangeRequestArgs, simplifiedResult: boolean): TextChange[] | protocol.CodeEdit[] { const { file, project } = this.getFileAndProject(args); + const scriptInfo = project.getScriptInfoForNormalizedPath(file)!; + const textRange = this.getRange(args, scriptInfo); - const textChanges = project.getLanguageService().toggleLineComment(file, args.textRanges); + const textChanges = project.getLanguageService().toggleLineComment(file, textRange); if (simplifiedResult) { const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!; @@ -2210,10 +2217,12 @@ namespace ts.server { return textChanges; } - private toggleMultilineComment(args: protocol.ToggleMultilineCommentRequestArgs, simplifiedResult: boolean): TextChange[] | protocol.CodeEdit[] { + private toggleMultilineComment(args: protocol.FileRangeRequestArgs, simplifiedResult: boolean): TextChange[] | protocol.CodeEdit[] { const { file, project } = this.getFileAndProject(args); + const scriptInfo = project.getScriptInfoForNormalizedPath(file)!; + const textRange = this.getRange(args, scriptInfo); - const textChanges = project.getLanguageService().toggleMultilineComment(file, args.textRanges); + const textChanges = project.getLanguageService().toggleMultilineComment(file, textRange); if (simplifiedResult) { const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!; @@ -2678,7 +2687,7 @@ namespace ts.server { [CommandNames.ToggleMultilineComment]: (request: protocol.ToggleMultilineCommentRequest) => { return this.requiredResponse(this.toggleMultilineComment(request.arguments, /*simplifiedResult*/true)); }, - [CommandNames.ToggleMultilineComment]: (request: protocol.ToggleMultilineCommentRequest) => { + [CommandNames.ToggleMultilineCommentFull]: (request: protocol.ToggleMultilineCommentRequest) => { return this.requiredResponse(this.toggleMultilineComment(request.arguments, /*simplifiedResult*/false)); }, }); diff --git a/src/services/services.ts b/src/services/services.ts index 9bc392c58d8..1e5f8f5043f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -5,8 +5,8 @@ namespace ts { function createNode(kind: TKind, pos: number, end: number, parent: Node): NodeObject | TokenObject | IdentifierObject | PrivateIdentifierObject { const node = isNodeKind(kind) ? new NodeObject(kind, pos, end) : kind === SyntaxKind.Identifier ? new IdentifierObject(SyntaxKind.Identifier, pos, end) : - kind === SyntaxKind.PrivateIdentifier ? new PrivateIdentifierObject(SyntaxKind.PrivateIdentifier, pos, end) : - new TokenObject(kind, pos, end); + kind === SyntaxKind.PrivateIdentifier ? new PrivateIdentifierObject(SyntaxKind.PrivateIdentifier, pos, end) : + new TokenObject(kind, pos, end); node.parent = parent; node.flags = parent.flags & NodeFlags.ContextFlags; return node; @@ -1985,62 +1985,58 @@ namespace ts { } } - function toggleLineComment(fileName: string, textRanges: TextRange[]): TextChange[] { + function toggleLineComment(fileName: string, textRange: TextRange): TextChange[] { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - const textChanges: TextChange[] = []; + const { lineStarts, firstLine, lastLine } = getLinesForRange(sourceFile, textRange); - 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/); + const isJsx = isInsideJsxElement(sourceFile, lineStarts[firstLine]) + const openComment = isJsx ? "{/*" : "//"; - let isCommenting = false; - let leftMostPosition = Number.MAX_VALUE; - let lineTextStarts = new Map(); - const whiteSpaceRegex = new RegExp(/\S/); - const isJsx = isInsideJsxElement(sourceFile, lineStarts[firstLine]) - const openComment = isJsx ? "{/*" : "//"; + // Check each line before any text changes. + for (let i = firstLine; i <= lastLine; i++) { + const lineText = sourceFile.text.substring(lineStarts[i], sourceFile.getLineEndOfPosition(lineStarts[i])); - // Check each line before any text changes. - for (let i = firstLine; i <= lastLine; i++) { - const lineText = sourceFile.text.substring(lineStarts[i], sourceFile.getLineEndOfPosition(lineStarts[i])); + // 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); - // 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); - - if (lineText.substr(regExec.index, openComment.length) !== openComment) { - isCommenting = true; - } + if (lineText.substr(regExec.index, openComment.length) !== openComment) { + isCommenting = true; } } + } - // Push all text changes. - 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 (isJsx) { - textChanges.push(...toggleMultilineComment(fileName, [{ pos: lineStarts[i] + leftMostPosition, end: sourceFile.getLineEndOfPosition(lineStarts[i]) }], isCommenting, isJsx)); - } else if (isCommenting) { - textChanges.push({ - newText: openComment, - span: { - length: 0, - start: lineStarts[i] + leftMostPosition - } - }); - } else { - textChanges.push({ - newText: "", - span: { - length: openComment.length, - start: lineStarts[i] + lineTextStart - } - }); - } + // Push all text changes. + 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 (isJsx) { + textChanges.push.apply(textChanges, toggleMultilineComment(fileName, { pos: lineStarts[i] + leftMostPosition, end: sourceFile.getLineEndOfPosition(lineStarts[i]) }, isCommenting, isJsx)); + } else if (isCommenting) { + textChanges.push({ + newText: openComment, + span: { + length: 0, + start: lineStarts[i] + leftMostPosition + } + }); + } else { + textChanges.push({ + newText: "", + span: { + length: openComment.length, + start: lineStarts[i] + lineTextStart + } + }); } } } @@ -2048,116 +2044,114 @@ namespace ts { return textChanges; } - function toggleMultilineComment(fileName: string, textRanges: TextRange[], insertComment?: boolean, isInsideJsx?: boolean): TextChange[] { + function toggleMultilineComment(fileName: string, textRange: TextRange, insertComment?: boolean, isInsideJsx?: boolean): TextChange[] { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); const textChanges: TextChange[] = []; const { text } = sourceFile; - for (const textRange of textRanges) { - let isCommenting = insertComment !== undefined ? insertComment : false; - const positions = [] as number[] as SortedArray; + let isCommenting = insertComment !== undefined ? insertComment : false; + const positions = [] as number[] as SortedArray; - let pos = textRange.pos; - const isJsx = isInsideJsx !== undefined ? isInsideJsx : isInsideJsxElement(sourceFile, pos); + let pos = textRange.pos; + const isJsx = isInsideJsx !== undefined ? isInsideJsx : isInsideJsxElement(sourceFile, pos); - const openMultiline = isJsx ? "{/*" : "/*"; - const closeMultiline = isJsx ? "*/}" : "*/"; - const openMultilineRegex = isJsx ? "\\{\\/\\*" : "\\/\\*"; - const closeMultilineRegex = isJsx ? "\\*\\/\\}" : "\\*\\/"; + 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); + // 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) { - // Comment range doesn't include the brace character. Increase it to include them. - if (isJsx) { - commentRange.pos--; - commentRange.end++; - } - - positions.push(commentRange.pos); - if (commentRange.kind === SyntaxKind.MultiLineCommentTrivia) { - positions.push(commentRange.end); - } - - pos = commentRange.end + 1; - } else { // If it's not in a comment range, then we need to comment the uncommented portions. - isCommenting = true; - - const newPos = text.substring(pos, textRange.end).search(`(${openMultilineRegex})|(${closeMultilineRegex})`); - pos = newPos === -1 ? textRange.end + 1 : pos + newPos + closeMultiline.length; + // If position is in a comment add it to the positions array. + if (commentRange) { + // Comment range doesn't include the brace character. Increase it to include them. + if (isJsx) { + commentRange.pos--; + commentRange.end++; } + + positions.push(commentRange.pos); + if (commentRange.kind === SyntaxKind.MultiLineCommentTrivia) { + positions.push(commentRange.end); + } + + pos = commentRange.end + 1; + } else { // If it's not in a comment range, then we need to comment the uncommented portions. + 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 + } + }); } - 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) { + // 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: lastPos + start: positions[i] } }); } - } else { - // If is not commenting then remove all comments found. - for (let i = 0; i < positions.length; i++) { - const offset = text.substr(positions[i] - closeMultiline.length, closeMultiline.length) === closeMultiline ? closeMultiline.length : 0; + + if (text.substr(positions[i], openMultiline.length) !== openMultiline) { textChanges.push({ - newText: "", + newText: openMultiline, span: { - length: openMultiline.length, - start: positions[i] - offset + 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 { + // If is not commenting then remove all comments found. + 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: openMultiline.length, + start: positions[i] - offset + } + }); + } } return textChanges; diff --git a/src/services/shims.ts b/src/services/shims.ts index 1e1669be605..5bf8e4235d7 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -278,8 +278,8 @@ namespace ts { getEmitOutput(fileName: string): string; getEmitOutputObject(fileName: string): EmitOutput; - toggleLineComment(fileName: string, textChanges: ts.TextRange[]): string; - toggleMultilineComment(fileName: string, textChanges: ts.TextRange[]): string; + toggleLineComment(fileName: string, textChange: ts.TextRange): string; + toggleMultilineComment(fileName: string, textChange: ts.TextRange): string; } export interface ClassifierShim extends Shim { @@ -1070,17 +1070,17 @@ namespace ts { this.logPerformance) as EmitOutput; } - public toggleLineComment(fileName: string, textRanges: ts.TextRange[]): string { + public toggleLineComment(fileName: string, textRange: ts.TextRange): string { return this.forwardJSONCall( - `toggleLineComment('${fileName}', '${JSON.stringify(textRanges)}')`, - () => this.languageService.toggleLineComment(fileName, textRanges) + `toggleLineComment('${fileName}', '${JSON.stringify(textRange)}')`, + () => this.languageService.toggleLineComment(fileName, textRange) ); } - public toggleMultilineComment(fileName: string, textRanges: ts.TextRange[]): string { + public toggleMultilineComment(fileName: string, textRange: ts.TextRange): string { return this.forwardJSONCall( - `toggleMultilineComment('${fileName}', '${JSON.stringify(textRanges)}')`, - () => this.languageService.toggleMultilineComment(fileName, textRanges) + `toggleMultilineComment('${fileName}', '${JSON.stringify(textRange)}')`, + () => this.languageService.toggleMultilineComment(fileName, textRange) ); } } diff --git a/src/services/types.ts b/src/services/types.ts index 39644aa9f0a..e508c3200da 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -486,8 +486,8 @@ namespace ts { /* @internal */ getNonBoundSourceFile(fileName: string): SourceFile; - toggleLineComment(fileName: string, textRanges: TextRange[]): TextChange[]; - toggleMultilineComment(fileName: string, textRanges: TextRange[]): TextChange[]; + toggleLineComment(fileName: string, textRanges: TextRange): TextChange[]; + toggleMultilineComment(fileName: string, textRanges: TextRange): TextChange[]; dispose(): void; }