in tsdoc/src/parser/NodeParser.ts [707:788]
private _parseBlockTag(tokenReader: TokenReader): DocNode {
tokenReader.assertAccumulatedSequenceIsEmpty();
const marker: number = tokenReader.createMarker();
if (tokenReader.peekTokenKind() !== TokenKind.AtSign) {
return this._backtrackAndCreateError(
tokenReader,
marker,
TSDocMessageId.MissingTag,
'Expecting a TSDoc tag starting with "@"'
);
}
// "@one" is a valid TSDoc tag at the start of a line, but "@one@two" is
// a syntax error. For two tags it should be "@one @two", or for literal text it
// should be "\@one\@two".
switch (tokenReader.peekPreviousTokenKind()) {
case TokenKind.EndOfInput:
case TokenKind.Spacing:
case TokenKind.Newline:
break;
default:
return this._backtrackAndCreateError(
tokenReader,
marker,
TSDocMessageId.AtSignInWord,
'The "@" character looks like part of a TSDoc tag; use a backslash to escape it'
);
}
// Include the "@" as part of the tagName
let tagName: string = tokenReader.readToken().toString();
if (tokenReader.peekTokenKind() !== TokenKind.AsciiWord) {
return this._backtrackAndCreateError(
tokenReader,
marker,
TSDocMessageId.AtSignWithoutTagName,
'Expecting a TSDoc tag name after "@"; if it is not a tag, use a backslash to escape this character'
);
}
const tagNameMarker: number = tokenReader.createMarker();
while (tokenReader.peekTokenKind() === TokenKind.AsciiWord) {
tagName += tokenReader.readToken().toString();
}
switch (tokenReader.peekTokenKind()) {
case TokenKind.Spacing:
case TokenKind.Newline:
case TokenKind.EndOfInput:
break;
default:
const badCharacter: string = tokenReader.peekToken().range.toString()[0];
return this._backtrackAndCreateError(
tokenReader,
marker,
TSDocMessageId.CharactersAfterBlockTag,
`The token "${tagName}" looks like a TSDoc tag but contains an invalid character` +
` ${JSON.stringify(badCharacter)}; if it is not a tag, use a backslash to escape the "@"`
);
}
if (StringChecks.explainIfInvalidTSDocTagName(tagName)) {
const failure: IFailure = this._createFailureForTokensSince(
tokenReader,
TSDocMessageId.MalformedTagName,
'A TSDoc tag name must start with a letter and contain only letters and numbers',
tagNameMarker
);
return this._backtrackAndCreateErrorForFailure(tokenReader, marker, '', failure);
}
return new DocBlockTag({
parsed: true,
configuration: this._configuration,
tagName,
tagNameExcerpt: tokenReader.extractAccumulatedSequence()
});
}