From 0defde71850fe75b4ee3ef7e5386fd1d4d374fc6 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 16 May 2017 15:04:33 -0700 Subject: [PATCH] Services utilities: Combine `isInsideComment` with `isInComment` --- src/services/completions.ts | 2 +- src/services/services.ts | 4 +- src/services/utilities.ts | 84 ++++++++++++------------------------- 3 files changed, 30 insertions(+), 60 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 5bc2a26861..d80eaa9a7e 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -359,7 +359,7 @@ namespace ts.Completions { start = timestamp(); // Completion not allowed inside comments, bail out if this is the case - const insideComment = isInsideComment(sourceFile, currentToken, position); + const insideComment = isInComment(sourceFile, position, currentToken); log("getCompletionData: Is inside comment: " + (timestamp() - start)); if (insideComment) { diff --git a/src/services/services.ts b/src/services/services.ts index 1b4678b5db..d35f1e7b8a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1853,8 +1853,8 @@ namespace ts { // OK, we have found a match in the file. This is only an acceptable match if // it is contained within a comment. - const token = getTokenAtPosition(sourceFile, matchPosition); - if (!isInsideComment(sourceFile, token, matchPosition)) { + + if (!isInComment(sourceFile, matchPosition)) { continue; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 4ae9204f7e..8a9976e00b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -256,37 +256,6 @@ namespace ts { getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node; } - /** Returns true if the position is within a comment */ - export function isInsideComment(sourceFile: SourceFile, token: Node, position: number): boolean { - // The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment - return position <= token.getStart(sourceFile) && - (isInsideCommentRange(getTrailingCommentRanges(sourceFile.text, token.getFullStart())) || - isInsideCommentRange(getLeadingCommentRanges(sourceFile.text, token.getFullStart()))); - - function isInsideCommentRange(comments: CommentRange[]): boolean { - return forEach(comments, comment => { - // either we are 1. completely inside the comment, or 2. at the end of the comment - if (comment.pos < position && position < comment.end) { - return true; - } - else if (position === comment.end) { - const text = sourceFile.text; - const width = comment.end - comment.pos; - // is single line comment or just /* - if (width <= 2 || text.charCodeAt(comment.pos + 1) === CharacterCodes.slash) { - return true; - } - else { - // is unterminated multi-line comment - return !(text.charCodeAt(comment.end - 1) === CharacterCodes.slash && - text.charCodeAt(comment.end - 2) === CharacterCodes.asterisk); - } - } - return false; - }); - } - } - export function getContainerNode(node: Node): Declaration { while (true) { node = node.parent; @@ -834,10 +803,6 @@ namespace ts { return false; } - export function isInComment(sourceFile: SourceFile, position: number) { - return isInCommentHelper(sourceFile, position, /*predicate*/ undefined); - } - /** * returns true if the position is in between the open and close elements of an JSX expression. */ @@ -883,15 +848,26 @@ namespace ts { } /** - * Returns true if the cursor at position in sourceFile is within a comment that additionally - * satisfies predicate, and false otherwise. + * Returns true if the cursor at position in sourceFile is within a comment. + * + * @param tokenAtPosition Must equal `getTokenAtPosition(sourceFile, position) + * @param predicate Additional predicate to test on the comment range. */ - export function isInCommentHelper(sourceFile: SourceFile, position: number, predicate?: (c: CommentRange) => boolean): boolean { - const token = getTokenAtPosition(sourceFile, position); + export function isInComment(sourceFile: SourceFile, position: number, tokenAtPosition = getTokenAtPosition(sourceFile, position), predicate?: (c: CommentRange) => boolean): boolean { + return position <= tokenAtPosition.getStart(sourceFile) && + (isInCommentRange(getLeadingCommentRanges(sourceFile.text, tokenAtPosition.pos)) || + isInCommentRange(getTrailingCommentRanges(sourceFile.text, tokenAtPosition.pos))); - if (token && position <= token.getStart(sourceFile)) { - const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos); + function isInCommentRange(commentRanges: CommentRange[]): boolean { + return forEach(commentRanges, c => isPositionInCommentRange(c, position, sourceFile.text) && (!predicate || predicate(c))); + } + } + function isPositionInCommentRange({ pos, end, kind }: ts.CommentRange, position: number, text: string): boolean { + if (pos < position && position < end) { + return true; + } + else if (position === end) { // The end marker of a single-line comment does not include the newline character. // In the following case, we are inside a comment (^ denotes the cursor position): // @@ -902,15 +878,13 @@ namespace ts { // /* asdf */^ // // Internally, we represent the end of the comment at the newline and closing '/', respectively. - return predicate ? - forEach(commentRanges, c => c.pos < position && - (c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end) && - predicate(c)) : - forEach(commentRanges, c => c.pos < position && - (c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end)); + return kind === SyntaxKind.SingleLineCommentTrivia || + // true for unterminated multi-line comment + !(text.charCodeAt(end - 1) === CharacterCodes.slash && text.charCodeAt(end - 2) === CharacterCodes.asterisk); + } + else { + return false; } - - return false; } export function hasDocComment(sourceFile: SourceFile, position: number) { @@ -1093,21 +1067,17 @@ namespace ts { } export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean { - return isInCommentHelper(sourceFile, position, isReferenceComment); - - function isReferenceComment(c: CommentRange): boolean { + return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => { const commentText = sourceFile.text.substring(c.pos, c.end); return tripleSlashDirectivePrefixRegex.test(commentText); - } + }); } export function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean { - return isInCommentHelper(sourceFile, position, isNonReferenceComment); - - function isNonReferenceComment(c: CommentRange): boolean { + return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => { const commentText = sourceFile.text.substring(c.pos, c.end); return !tripleSlashDirectivePrefixRegex.test(commentText); - } + }); } export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan {