From b02963b2380f8cd4c2675a72f974552e5acd7f1e Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 9 Jun 2017 14:43:28 -0700 Subject: [PATCH] make indent work with trailing comments --- src/harness/fourslash.ts | 8 +-- src/harness/harnessLanguageService.ts | 2 +- src/server/client.ts | 2 +- src/server/session.ts | 2 +- src/services/formatting/formatting.ts | 35 +++++++---- src/services/formatting/smartIndenter.ts | 18 +++--- src/services/services.ts | 6 +- src/services/shims.ts | 2 +- src/services/types.ts | 2 +- tests/cases/fourslash/isInMultiLineComment.ts | 58 +++++++++++-------- 10 files changed, 79 insertions(+), 56 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 54b5b48da2b..080614fc623 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2500,13 +2500,13 @@ namespace FourSlash { } } - public verifyisInMultiLineCommentAtPosition(negative: boolean) { + public verifyIsInMultiLineCommentAtPosition(negative: boolean) { const expected = !negative; const position = this.currentCaretPosition; const fileName = this.activeFile.fileName; - const actual = this.languageService.getisInMultiLineCommentAtPosition(fileName, position); + const actual = this.languageService.isInMultiLineCommentAtPosition(fileName, position); if (expected !== actual) { - this.raiseError(`verifyIsInDocComment failed: at position '${position}' in '${fileName}', expected '${expected}'.`); + this.raiseError(`verifyIsInMultiLineCommentAtPosition failed: at position '${position}' in '${fileName}', expected '${expected}'.`); } } @@ -3584,7 +3584,7 @@ namespace FourSlashInterface { } public isInMultiLineCommentAtPosition() { - this.state.verifyisInMultiLineCommentAtPosition(this.negative); + this.state.verifyIsInMultiLineCommentAtPosition(this.negative); } public codeFixAvailable() { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index c3a09b3926f..962f9a0df5d 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -486,7 +486,7 @@ namespace Harness.LanguageService { isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean { return unwrapJSONCallResult(this.shim.isValidBraceCompletionAtPosition(fileName, position, openingBrace)); } - getisInMultiLineCommentAtPosition(fileName: string, position: number): boolean { + isInMultiLineCommentAtPosition(fileName: string, position: number): boolean { return unwrapJSONCallResult(this.shim.getisInMultiLineCommentAtPosition(fileName, position)); } getCodeFixesAtPosition(): ts.CodeAction[] { diff --git a/src/server/client.ts b/src/server/client.ts index 5a162c1041d..0e66516da40 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -676,7 +676,7 @@ namespace ts.server { return notImplemented(); } - getisInMultiLineCommentAtPosition(_fileName: string, _position: number): boolean { + isInMultiLineCommentAtPosition(_fileName: string, _position: number): boolean { return notImplemented(); } diff --git a/src/server/session.ts b/src/server/session.ts index e4e900ddce7..9657c666238 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -965,7 +965,7 @@ namespace ts.server { const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); - return project.getLanguageService(/*ensureSynchronized*/ false).getisInMultiLineCommentAtPosition(file, position); + return project.getLanguageService(/*ensureSynchronized*/ false).isInMultiLineCommentAtPosition(file, position); } private getIndentation(args: protocol.IndentationRequestArgs) { diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index fa49ee852b1..7feba4b7dd2 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -1118,23 +1118,36 @@ namespace ts.formatting { } /** - * @returns -1 iff the position is not in a multi-line comment. + * Gets the indentation level of the multi-line comment enclosing position, + * and a negative value if the position is not in a multi-line comment. */ - export function getIndentationOfEnclosingMultiLineComment(sourceFile: SourceFile, position: number): number { - const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false); - const leadingCommentRanges = getLeadingCommentRangesOfNode(token, sourceFile); - if (leadingCommentRanges) { - loop: for (const range of leadingCommentRanges) { + export function getIndentationOfEnclosingMultiLineComment(sourceFile: SourceFile, position: number, options: EditorSettings): number { + const range = getRangeOfEnclosingComment(sourceFile, position, SyntaxKind.MultiLineCommentTrivia); + if (range) { + const commentStart = range.pos; + const commentLineStart = getLineStartPositionForPosition(commentStart, sourceFile); + const { column, character } = SmartIndenter.findFirstNonWhitespaceCharacterAndColumn(commentLineStart, commentStart, sourceFile, options); + return range.pos - character + column; + } + return undefined; + } + + export function getRangeOfEnclosingComment(sourceFile: SourceFile, position: number, kind: CommentKind): CommentRange | undefined { + const precedingToken = findPrecedingToken(position, sourceFile); + const trailingRangesOfPreviousToken = precedingToken && getTrailingCommentRanges(sourceFile.text, precedingToken.end); + const leadingCommentRangesOfNextToken = getLeadingCommentRangesOfNode(getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false), sourceFile); + const commentRanges = trailingRangesOfPreviousToken && leadingCommentRangesOfNextToken ? + trailingRangesOfPreviousToken.concat(leadingCommentRangesOfNextToken) : + trailingRangesOfPreviousToken || leadingCommentRangesOfNextToken; + if (commentRanges) { + for (const range of commentRanges) { // We need to extend the range when in an unclosed multi-line comment. if (range.pos < position && (position < range.end || position === range.end && position === sourceFile.getFullWidth())) { - if (range.kind === SyntaxKind.MultiLineCommentTrivia) { - return range.pos - getLineStartPositionForPosition(range.pos, sourceFile); - } - break loop; + return range.kind === kind ? range : undefined; } } } - return -1; + return undefined; } diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 8acab0e6603..c5aee567a41 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -44,8 +44,8 @@ namespace ts.formatting { const lineAtPosition = sourceFile.getLineAndCharacterOfPosition(position).line; - const indentationOfEnclosingMultiLineComment = getIndentationOfEnclosingMultiLineComment(sourceFile, position); - if (indentationOfEnclosingMultiLineComment !== -1) { + const indentationOfEnclosingMultiLineComment = getIndentationOfEnclosingMultiLineComment(sourceFile, position, options); + if (indentationOfEnclosingMultiLineComment >= 0) { return indentationOfEnclosingMultiLineComment; } @@ -410,13 +410,13 @@ namespace ts.formatting { return findFirstNonWhitespaceColumn(lineStart, lineStart + lineAndCharacter.character, sourceFile, options); } - /* - Character is the actual index of the character since the beginning of the line. - Column - position of the character after expanding tabs to spaces - "0\t2$" - value of 'character' for '$' is 3 - value of 'column' for '$' is 6 (assuming that tab size is 4) - */ + /** + * Character is the actual index of the character since the beginning of the line. + * Column - position of the character after expanding tabs to spaces. + * "0\t2$" + * value of 'character' for '$' is 3 + * value of 'column' for '$' is 6 (assuming that tab size is 4) + */ export function findFirstNonWhitespaceCharacterAndColumn(startPos: number, endPos: number, sourceFile: SourceFileLike, options: EditorSettings) { let character = 0; let column = 0; diff --git a/src/services/services.ts b/src/services/services.ts index ab398db7c35..294afec6029 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1818,9 +1818,9 @@ namespace ts { return true; } - function getisInMultiLineCommentAtPosition(fileName: string, position: number): boolean { + function isInMultiLineCommentAtPosition(fileName: string, position: number): boolean { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - return ts.formatting.getIndentationOfEnclosingMultiLineComment(sourceFile, position) !== -1; + return !!ts.formatting.getRangeOfEnclosingComment(sourceFile, position, SyntaxKind.MultiLineCommentTrivia); } function getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] { @@ -2045,7 +2045,7 @@ namespace ts { getFormattingEditsAfterKeystroke, getDocCommentTemplateAtPosition, isValidBraceCompletionAtPosition, - getisInMultiLineCommentAtPosition, + isInMultiLineCommentAtPosition, getCodeFixesAtPosition, getEmitOutput, getNonBoundSourceFile, diff --git a/src/services/shims.ts b/src/services/shims.ts index 75c5e8445c1..fd2ba1cca72 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -844,7 +844,7 @@ namespace ts { public getisInMultiLineCommentAtPosition(fileName: string, position: number): string { return this.forwardJSONCall( `getisInMultiLineCommentAtPosition('${fileName}', ${position})`, - () => this.languageService.getisInMultiLineCommentAtPosition(fileName, position) + () => this.languageService.isInMultiLineCommentAtPosition(fileName, position) ); } diff --git a/src/services/types.ts b/src/services/types.ts index 3a7a14d3dbc..bbd9fa9986c 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -260,7 +260,7 @@ namespace ts { isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; - getisInMultiLineCommentAtPosition(fileName: string, position: number): boolean; + isInMultiLineCommentAtPosition(fileName: string, position: number): boolean; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: FormatCodeSettings): CodeAction[]; getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[]; diff --git a/tests/cases/fourslash/isInMultiLineComment.ts b/tests/cases/fourslash/isInMultiLineComment.ts index 39aac71a8bd..58e4e4d3337 100644 --- a/tests/cases/fourslash/isInMultiLineComment.ts +++ b/tests/cases/fourslash/isInMultiLineComment.ts @@ -1,36 +1,46 @@ /// //// /* x */ -//// /** x */ +//// /** +//// * @param this doesn't make sense here. +//// */ //// // x -//// let x = 1; +//// let x = 1; /* +//// * +const firstCommentStart = 0; +const firstCommentEnd = 7; +goTo.position(firstCommentStart); +verify.not.isInMultiLineCommentAtPosition(); -for (let i = 1; i < 7; ++i) { - goTo.position(i); - verify.isInMultiLineCommentAtPosition(); -} +goTo.position(firstCommentStart + 1); +verify.isInMultiLineCommentAtPosition(); +goTo.position(firstCommentEnd - 1); +verify.isInMultiLineCommentAtPosition(); -for (let i = 0; i < 2; ++i) { - goTo.position(i * 7); - verify.not.isInMultiLineCommentAtPosition(); -} +goTo.position(firstCommentEnd); +verify.not.isInMultiLineCommentAtPosition(); -const jsDocStart = 8; +const multilineJsDocStart = firstCommentEnd + 1; +const multilineJsDocEnd = multilineJsDocStart + 49; -for (let i = 1; i < 8; ++i) { - goTo.position(jsDocStart + i); - verify.isInMultiLineCommentAtPosition(); -} +goTo.position(multilineJsDocStart); +verify.not.isInMultiLineCommentAtPosition(); +goTo.position(multilineJsDocStart + 1); +verify.isInMultiLineCommentAtPosition(); +goTo.position(multilineJsDocEnd - 1); +verify.isInMultiLineCommentAtPosition(); +goTo.position(multilineJsDocEnd); +verify.not.isInMultiLineCommentAtPosition(); -for (let i = 0; i < 2; ++i) { - goTo.position(jsDocStart + i * 8); - verify.not.isInMultiLineCommentAtPosition(); -} +const singleLineCommentStart = multilineJsDocEnd + 1; -const singleLineCommentStart = 17; +goTo.position(singleLineCommentStart + 1); +verify.not.isInMultiLineCommentAtPosition(); -for (let i = 0; i < 5; ++i) { - goTo.position(singleLineCommentStart + i); - verify.not.isInMultiLineCommentAtPosition(); -} +const postNodeCommentStart = singleLineCommentStart + 16; + +goTo.position(postNodeCommentStart); +verify.not.isInMultiLineCommentAtPosition(); +goTo.position(postNodeCommentStart + 1); +verify.isInMultiLineCommentAtPosition();