Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
84 changes: 27 additions & 57 deletions src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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):
//
Expand All @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down