private _parseParamBlock()

in tsdoc/src/parser/NodeParser.ts [506:656]


  private _parseParamBlock(
    tokenReader: TokenReader,
    docBlockTag: DocBlockTag,
    tagName: string
  ): DocParamBlock {
    const startMarker: number = tokenReader.createMarker();

    const spacingBeforeParameterNameExcerpt: TokenSequence | undefined = this._tryReadSpacingAndNewlines(
      tokenReader
    );

    // Skip past a JSDoc type (i.e., '@param {type} paramName') if found, and report a warning.
    const unsupportedJsdocTypeBeforeParameterNameExcerpt:
      | TokenSequence
      | undefined = this._tryParseUnsupportedJSDocType(tokenReader, docBlockTag, tagName);

    // Parse opening of invalid JSDoc optional parameter name (e.g., '[')
    let unsupportedJsdocOptionalNameOpenBracketExcerpt: TokenSequence | undefined;
    if (tokenReader.peekTokenKind() === TokenKind.LeftSquareBracket) {
      tokenReader.readToken(); // read the "["
      unsupportedJsdocOptionalNameOpenBracketExcerpt = tokenReader.extractAccumulatedSequence();
    }

    let parameterName: string = '';

    let done: boolean = false;
    while (!done) {
      switch (tokenReader.peekTokenKind()) {
        case TokenKind.AsciiWord:
        case TokenKind.Period:
        case TokenKind.DollarSign:
          parameterName += tokenReader.readToken();
          break;
        default:
          done = true;
          break;
      }
    }

    const explanation: string | undefined = StringChecks.explainIfInvalidUnquotedIdentifier(parameterName);

    if (explanation !== undefined) {
      tokenReader.backtrackToMarker(startMarker);

      const errorParamBlock: DocParamBlock = new DocParamBlock({
        configuration: this._configuration,
        blockTag: docBlockTag,
        parameterName: ''
      });
      const errorMessage: string =
        parameterName.length > 0
          ? 'The ' + tagName + ' block should be followed by a valid parameter name: ' + explanation
          : 'The ' + tagName + ' block should be followed by a parameter name';

      this._parserContext.log.addMessageForTokenSequence(
        TSDocMessageId.ParamTagWithInvalidName,
        errorMessage,
        docBlockTag.getTokenSequence(),
        docBlockTag
      );
      return errorParamBlock;
    }

    const parameterNameExcerpt: TokenSequence = tokenReader.extractAccumulatedSequence();

    // Parse closing of invalid JSDoc optional parameter name (e.g., ']', '=default]').
    let unsupportedJsdocOptionalNameRestExcerpt: TokenSequence | undefined;
    if (unsupportedJsdocOptionalNameOpenBracketExcerpt) {
      unsupportedJsdocOptionalNameRestExcerpt = this._tryParseJSDocOptionalNameRest(tokenReader);

      let errorSequence: TokenSequence | undefined = unsupportedJsdocOptionalNameOpenBracketExcerpt;
      if (unsupportedJsdocOptionalNameRestExcerpt) {
        errorSequence = docBlockTag
          .getTokenSequence()
          .getNewSequence(
            unsupportedJsdocOptionalNameOpenBracketExcerpt.startIndex,
            unsupportedJsdocOptionalNameRestExcerpt.endIndex
          );
      }

      this._parserContext.log.addMessageForTokenSequence(
        TSDocMessageId.ParamTagWithInvalidOptionalName,
        'The ' +
          tagName +
          " should not include a JSDoc-style optional name; it must not be enclosed in '[ ]' brackets.",
        errorSequence,
        docBlockTag
      );
    }

    const spacingAfterParameterNameExcerpt: TokenSequence | undefined = this._tryReadSpacingAndNewlines(
      tokenReader
    );

    // Skip past a trailing JSDoc type (i.e., '@param paramName {type}') if found, and report a warning.
    const unsupportedJsdocTypeAfterParameterNameExcerpt:
      | TokenSequence
      | undefined = this._tryParseUnsupportedJSDocType(tokenReader, docBlockTag, tagName);

    // TODO: Warn if there is no space before or after the hyphen
    let hyphenExcerpt: TokenSequence | undefined;
    let spacingAfterHyphenExcerpt: TokenSequence | undefined;
    let unsupportedJsdocTypeAfterHyphenExcerpt: TokenSequence | undefined;
    if (tokenReader.peekTokenKind() === TokenKind.Hyphen) {
      tokenReader.readToken();
      hyphenExcerpt = tokenReader.extractAccumulatedSequence();
      // TODO: Only read one space
      spacingAfterHyphenExcerpt = this._tryReadSpacingAndNewlines(tokenReader);

      // Skip past a JSDoc type (i.e., '@param paramName - {type}') if found, and report a warning.
      unsupportedJsdocTypeAfterHyphenExcerpt = this._tryParseUnsupportedJSDocType(
        tokenReader,
        docBlockTag,
        tagName
      );
    } else {
      this._parserContext.log.addMessageForTokenSequence(
        TSDocMessageId.ParamTagMissingHyphen,
        'The ' + tagName + ' block should be followed by a parameter name and then a hyphen',
        docBlockTag.getTokenSequence(),
        docBlockTag
      );
    }

    return new DocParamBlock({
      parsed: true,
      configuration: this._configuration,

      blockTag: docBlockTag,

      spacingBeforeParameterNameExcerpt,

      unsupportedJsdocTypeBeforeParameterNameExcerpt,
      unsupportedJsdocOptionalNameOpenBracketExcerpt,

      parameterNameExcerpt,
      parameterName,

      unsupportedJsdocOptionalNameRestExcerpt,

      spacingAfterParameterNameExcerpt,

      unsupportedJsdocTypeAfterParameterNameExcerpt,

      hyphenExcerpt,

      spacingAfterHyphenExcerpt,

      unsupportedJsdocTypeAfterHyphenExcerpt
    });
  }