Services utilities: Combine isInsideComment with isInComment

This commit is contained in:
Andy Hanson 2017-05-16 15:04:33 -07:00
parent 278fb803b1
commit 0defde7185
3 changed files with 30 additions and 60 deletions

View file

@ -359,7 +359,7 @@ namespace ts.Completions {
start = timestamp(); start = timestamp();
// Completion not allowed inside comments, bail out if this is the case // 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)); log("getCompletionData: Is inside comment: " + (timestamp() - start));
if (insideComment) { if (insideComment) {

View file

@ -1853,8 +1853,8 @@ namespace ts {
// OK, we have found a match in the file. This is only an acceptable match if // OK, we have found a match in the file. This is only an acceptable match if
// it is contained within a comment. // it is contained within a comment.
const token = getTokenAtPosition(sourceFile, matchPosition);
if (!isInsideComment(sourceFile, token, matchPosition)) { if (!isInComment(sourceFile, matchPosition)) {
continue; continue;
} }

View file

@ -256,37 +256,6 @@ namespace ts {
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node; 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 { export function getContainerNode(node: Node): Declaration {
while (true) { while (true) {
node = node.parent; node = node.parent;
@ -834,10 +803,6 @@ namespace ts {
return false; 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. * 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 * Returns true if the cursor at position in sourceFile is within a comment.
* satisfies predicate, and false otherwise. *
* @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 { export function isInComment(sourceFile: SourceFile, position: number, tokenAtPosition = getTokenAtPosition(sourceFile, position), predicate?: (c: CommentRange) => boolean): boolean {
const token = getTokenAtPosition(sourceFile, position); return position <= tokenAtPosition.getStart(sourceFile) &&
(isInCommentRange(getLeadingCommentRanges(sourceFile.text, tokenAtPosition.pos)) ||
isInCommentRange(getTrailingCommentRanges(sourceFile.text, tokenAtPosition.pos)));
if (token && position <= token.getStart(sourceFile)) { function isInCommentRange(commentRanges: CommentRange[]): boolean {
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos); 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. // 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): // In the following case, we are inside a comment (^ denotes the cursor position):
// //
@ -902,15 +878,13 @@ namespace ts {
// /* asdf */^ // /* asdf */^
// //
// Internally, we represent the end of the comment at the newline and closing '/', respectively. // Internally, we represent the end of the comment at the newline and closing '/', respectively.
return predicate ? return kind === SyntaxKind.SingleLineCommentTrivia ||
forEach(commentRanges, c => c.pos < position && // true for unterminated multi-line comment
(c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end) && !(text.charCodeAt(end - 1) === CharacterCodes.slash && text.charCodeAt(end - 2) === CharacterCodes.asterisk);
predicate(c)) : }
forEach(commentRanges, c => c.pos < position && else {
(c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end)); return false;
} }
return false;
} }
export function hasDocComment(sourceFile: SourceFile, position: number) { export function hasDocComment(sourceFile: SourceFile, position: number) {
@ -1093,21 +1067,17 @@ namespace ts {
} }
export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean { export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInCommentHelper(sourceFile, position, isReferenceComment); return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => {
function isReferenceComment(c: CommentRange): boolean {
const commentText = sourceFile.text.substring(c.pos, c.end); const commentText = sourceFile.text.substring(c.pos, c.end);
return tripleSlashDirectivePrefixRegex.test(commentText); return tripleSlashDirectivePrefixRegex.test(commentText);
} });
} }
export function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean { export function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInCommentHelper(sourceFile, position, isNonReferenceComment); return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => {
function isNonReferenceComment(c: CommentRange): boolean {
const commentText = sourceFile.text.substring(c.pos, c.end); const commentText = sourceFile.text.substring(c.pos, c.end);
return !tripleSlashDirectivePrefixRegex.test(commentText); return !tripleSlashDirectivePrefixRegex.test(commentText);
} });
} }
export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan { export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan {