@@ -8,7 +8,7 @@ import { IDocElement, IParam, IHrefLinkElement, ICodeLinkElement, ITextElement }
88import { IDocItem , IDocFunction } from '../IDocItem' ;
99import DocItemLoader from '../DocItemLoader' ;
1010import { IApiDefinitionReference } from '../IApiDefinitionReference' ;
11- import Token from '../Token' ;
11+ import Token , { TokenType } from '../Token' ;
1212import Tokenizer from '../Tokenizer' ;
1313
1414/**
@@ -64,15 +64,13 @@ export default class ApiDocumentation {
6464 // For guidance about using these tags, please see this document:
6565 // https://onedrive.visualstudio.com/DefaultCollection/SPPPlat/_git/sp-client
6666 // ?path=/common/docs/ApiPrinciplesAndProcess.md
67- private static _allowedJsdocTags : string [ ] = [
67+ private static _allowedRegularJsdocTags : string [ ] = [
6868 // (alphabetical order)
6969 '@alpha' ,
7070 '@beta' ,
7171 '@betadocumentation' ,
72- '@inheritdoc' ,
7372 '@internal' ,
7473 '@internalremarks' ,
75- '@link' ,
7674 '@param' ,
7775 '@preapproved' ,
7876 '@public' ,
@@ -84,6 +82,12 @@ export default class ApiDocumentation {
8482 '@remarks'
8583 ] ;
8684
85+ private static _allowedInlineJsdocTags : string [ ] = [
86+ // (alphabetical order)
87+ '@inheritdoc' ,
88+ '@link'
89+ ] ;
90+
8791 /**
8892 * Match JsDoc block tags and inline tags
8993 * Example "@a @b@c d@e @f {whatever} {@link a} { @something } \@g" => ["@a", "@f", "{@link a}", "{ @something }"]
@@ -282,14 +286,12 @@ export default class ApiDocumentation {
282286 // if this documentation inherits docs from a deprecated API item, then
283287 // this documentation must either have a deprecated message or it must
284288 // not use the @inheritdoc and copy+paste the documentation
285- this . reportError ( `Use of @inheritdoc API item reference that is deprecated. ` +
286- 'Either include @deprecated JSDoc with message on this item or remove the @inheritdoc tag ' +
287- 'and copy+paste the documentation.' ) ;
289+ this . reportError ( `A deprecation message must be included after the @deprecated tag.` ) ;
288290 }
289291 break ;
290292 }
291293
292- if ( token . type === ' Tag' ) {
294+ if ( token . type === TokenType . Tag ) {
293295 switch ( token . tag ) {
294296 case '@remarks' :
295297 tokenizer . getToken ( ) ;
@@ -355,15 +357,9 @@ export default class ApiDocumentation {
355357 break ;
356358 default :
357359 tokenizer . getToken ( ) ;
358- if ( ApiDocumentation . _allowedJsdocTags . indexOf ( token . tag ) < 0 ) {
359- this . reportError ( `The JSDoc tag \"${ token . tag } \" is not allowed` ) ;
360- break ;
361- } else {
362- this . reportError ( `Error formatting token tag: ${ token . tag } ` ) ;
363- break ;
364- }
360+ this . _reportBadJSDocTag ( token ) ;
365361 }
366- } else if ( token . type === ' Inline' ) {
362+ } else if ( token . type === TokenType . Inline ) {
367363 switch ( token . tag ) {
368364 case '@inheritdoc' :
369365 tokenizer . getToken ( ) ;
@@ -383,15 +379,21 @@ export default class ApiDocumentation {
383379 break ;
384380 default :
385381 tokenizer . getToken ( ) ;
386- this . reportError ( `Unidentifiable inline token ${ token . tag } ` ) ;
382+ this . _reportBadJSDocTag ( token ) ;
387383 break ;
388384 }
389- } else if ( token . type === ' Text' ) {
385+ } else if ( token . type === TokenType . Text ) {
390386 tokenizer . getToken ( ) ;
391- this . reportError ( 'Unexpected text. Text must either be the first sentences of the JSDoc, or if too long for ' +
392- 'the first 2-3 sentences the text must be preceded by a @internalremarks tag.' ) ;
387+ // Shorten "This is too long text" to "This is..."
388+ const MAX_LENGTH : number = 40 ;
389+ let problemText : string = token . text . trim ( ) ;
390+ if ( problemText . length > MAX_LENGTH ) {
391+ problemText = problemText . substr ( 0 , MAX_LENGTH - 3 ) . trim ( ) + '...' ;
392+ }
393+ this . reportError ( `Unexpected text in JSDoc comment: "${ problemText } "` ) ;
393394 } else {
394395 tokenizer . getToken ( ) ;
396+ // This would be a program bug
395397 this . reportError ( `Unexpected token: ${ token . type } ${ token . tag } ${ token . text } ` ) ;
396398 }
397399 }
@@ -474,37 +476,59 @@ export default class ApiDocumentation {
474476 }
475477
476478 protected _parseParam ( tokenizer : Tokenizer ) : IParam {
477- const paramDescriptionToken : Token = tokenizer . getToken ( ) ;
478- if ( ! paramDescriptionToken ) {
479- this . reportError ( '@param tag missing required description' ) ;
480- return ;
481- }
482- const hyphenIndex : number = paramDescriptionToken ? paramDescriptionToken . text . indexOf ( '-' ) : - 1 ;
483- if ( hyphenIndex < 0 ) {
484- this . reportError ( 'No hyphens found in the @param line. ' +
485- 'There should be a hyphen between the parameter name and its description.' ) ;
486- return ;
487- } else {
488- const name : string = paramDescriptionToken . text . slice ( 0 , hyphenIndex ) . trim ( ) ;
489- const comment : string = paramDescriptionToken . text . substr ( hyphenIndex + 1 ) . trim ( ) ;
490-
491- if ( ! comment ) {
492- this . reportError ( '@param tag requires a description following the hyphen' ) ;
493- return ;
494- }
495-
496- const commentTextElement : IDocElement = DocElementParser . makeTextElement ( comment ) ;
497- // Full param description may contain additional Tokens (Ex: @link)
498- const remainingElements : IDocElement [ ] = DocElementParser . parse ( tokenizer , this . reportError ) ;
499- const descriptionElements : IDocElement [ ] = [ commentTextElement ] . concat ( remainingElements ) ;
500-
501- const paramDocElement : IParam = {
502- name : name ,
503- description : descriptionElements
504- } ;
505- return paramDocElement ;
506- }
479+ const paramDescriptionToken : Token = tokenizer . getToken ( ) ;
480+ if ( ! paramDescriptionToken ) {
481+ this . reportError ( '@param tag missing required description' ) ;
482+ return ;
507483 }
484+ const hyphenIndex : number = paramDescriptionToken ? paramDescriptionToken . text . indexOf ( '-' ) : - 1 ;
485+ if ( hyphenIndex < 0 ) {
486+ this . reportError ( 'No hyphens found in the @param line. ' +
487+ 'There should be a hyphen between the parameter name and its description.' ) ;
488+ return ;
489+ } else {
490+ const name : string = paramDescriptionToken . text . slice ( 0 , hyphenIndex ) . trim ( ) ;
491+ const comment : string = paramDescriptionToken . text . substr ( hyphenIndex + 1 ) . trim ( ) ;
492+
493+ if ( ! comment ) {
494+ this . reportError ( '@param tag requires a description following the hyphen' ) ;
495+ return ;
496+ }
497+
498+ const commentTextElement : IDocElement = DocElementParser . makeTextElement ( comment ) ;
499+ // Full param description may contain additional Tokens (Ex: @link)
500+ const remainingElements : IDocElement [ ] = DocElementParser . parse ( tokenizer , this . reportError ) ;
501+ const descriptionElements : IDocElement [ ] = [ commentTextElement ] . concat ( remainingElements ) ;
502+
503+ const paramDocElement : IParam = {
504+ name : name ,
505+ description : descriptionElements
506+ } ;
507+ return paramDocElement ;
508+ }
509+ }
510+
511+ private _reportBadJSDocTag ( token : Token ) : void {
512+ const supportsRegular : boolean = ApiDocumentation . _allowedRegularJsdocTags . indexOf ( token . tag ) >= 0 ;
513+ const supportsInline : boolean = ApiDocumentation . _allowedInlineJsdocTags . indexOf ( token . tag ) >= 0 ;
514+
515+ if ( ! supportsRegular && ! supportsInline ) {
516+ this . reportError ( `Unknown JSDoc tag \"${ token . tag } \"` ) ;
517+ return ;
518+ }
519+
520+ if ( token . type === TokenType . Inline && ! supportsInline ) {
521+ this . reportError ( `The JSDoc tag \"${ token . tag } \" must not use the non-inline syntax (no curly braces)` ) ;
522+ return ;
523+ }
524+ if ( token . type === TokenType . Tag && ! supportsRegular ) {
525+ this . reportError ( `The JSDoc tag \"${ token . tag } \" must use the inline syntax (with curly braces)` ) ;
526+ return ;
527+ }
528+
529+ this . reportError ( `The JSDoc tag \"${ token . tag } \" is not supported in this context` ) ;
530+ return ;
531+ }
508532
509533 private _checkInheritDocStatus ( ) : void {
510534 if ( this . isDocInherited ) {
0 commit comments