in tsdoc/src/parser/LineExtractor.ts [32:173]
public static extract(parserContext: ParserContext): boolean {
const range: TextRange = parserContext.sourceRange;
const buffer: string = range.buffer;
let commentRangeStart: number = 0;
let commentRangeEnd: number = 0;
// These must be set before entering CollectingFirstLine, CollectingLine, or AdvancingLine
let collectingLineStart: number = 0;
let collectingLineEnd: number = 0;
let nextIndex: number = range.pos;
let state: State = State.BeginComment1;
const lines: TextRange[] = [];
while (state !== State.Done) {
if (nextIndex >= range.end) {
// reached the end of the input
switch (state) {
case State.BeginComment1:
case State.BeginComment2:
parserContext.log.addMessageForTextRange(
TSDocMessageId.CommentNotFound,
'Expecting a "/**" comment',
range
);
return false;
default:
parserContext.log.addMessageForTextRange(
TSDocMessageId.CommentMissingClosingDelimiter,
'Unexpected end of input',
range
);
return false;
}
}
const current: string = buffer[nextIndex];
const currentIndex: number = nextIndex;
++nextIndex;
const next: string = nextIndex < range.end ? buffer[nextIndex] : '';
switch (state) {
case State.BeginComment1:
if (current === '/' && next === '*') {
commentRangeStart = currentIndex;
++nextIndex; // skip the star
state = State.BeginComment2;
} else if (!LineExtractor._whitespaceCharacterRegExp.test(current)) {
parserContext.log.addMessageForTextRange(
TSDocMessageId.CommentOpeningDelimiterSyntax,
'Expecting a leading "/**"',
range.getNewRange(currentIndex, currentIndex + 1)
);
return false;
}
break;
case State.BeginComment2:
if (current === '*') {
if (next === ' ') {
++nextIndex; // Discard the space after the star
}
collectingLineStart = nextIndex;
collectingLineEnd = nextIndex;
state = State.CollectingFirstLine;
} else {
parserContext.log.addMessageForTextRange(
TSDocMessageId.CommentOpeningDelimiterSyntax,
'Expecting a leading "/**"',
range.getNewRange(currentIndex, currentIndex + 1)
);
return false;
}
break;
case State.CollectingFirstLine:
case State.CollectingLine:
if (current === '\n') {
// Ignore an empty line if it is immediately after the "/**"
if (state !== State.CollectingFirstLine || collectingLineEnd > collectingLineStart) {
// Record the line that we collected
lines.push(range.getNewRange(collectingLineStart, collectingLineEnd));
}
collectingLineStart = nextIndex;
collectingLineEnd = nextIndex;
state = State.AdvancingLine;
} else if (current === '*' && next === '/') {
if (collectingLineEnd > collectingLineStart) {
lines.push(range.getNewRange(collectingLineStart, collectingLineEnd));
}
collectingLineStart = 0;
collectingLineEnd = 0;
++nextIndex; // skip the slash
commentRangeEnd = nextIndex;
state = State.Done;
} else if (!LineExtractor._whitespaceCharacterRegExp.test(current)) {
collectingLineEnd = nextIndex;
}
break;
case State.AdvancingLine:
if (current === '*') {
if (next === '/') {
collectingLineStart = 0;
collectingLineEnd = 0;
++nextIndex; // skip the slash
commentRangeEnd = nextIndex;
state = State.Done;
} else {
// Discard the "*" at the start of a line
if (next === ' ') {
++nextIndex; // Discard the space after the star
}
collectingLineStart = nextIndex;
collectingLineEnd = nextIndex;
state = State.CollectingLine;
}
} else if (current === '\n') {
// Blank line
lines.push(range.getNewRange(currentIndex, currentIndex));
collectingLineStart = nextIndex;
} else if (!LineExtractor._whitespaceCharacterRegExp.test(current)) {
// If the star is missing, then start the line here
// Example: "/**\nL1*/"
// (collectingLineStart was the start of this line)
collectingLineEnd = nextIndex;
state = State.CollectingLine;
}
break;
}
}
/**
* Only fill in these if we successfully scanned a comment
*/
parserContext.commentRange = range.getNewRange(commentRangeStart, commentRangeEnd);
parserContext.lines = lines;
return true;
}