@@ -6151,10 +6151,10 @@ namespace ts {
61516151 return comment ;
61526152 }
61536153
6154- const enum TagState {
6154+ const enum JSDocState {
61556155 BeginningOfLine ,
61566156 SawAsterisk ,
6157- SavingComments
6157+ SavingComments ,
61586158 }
61596159
61606160 export function parseJSDocCommentWorker ( start : number , length : number ) : JSDoc {
@@ -6180,93 +6180,81 @@ namespace ts {
61806180 scanner . scanRange ( start + 3 , length - 5 , ( ) => {
61816181 // Initially we can parse out a tag. We also have seen a starting asterisk.
61826182 // This is so that /** * @type */ doesn't parse.
6183- let canParseTag = true ;
6184- let seenAsterisk = true ;
61856183 let advanceToken = true ;
6184+ let state = JSDocState . SawAsterisk ;
61866185 let margin : number | undefined = undefined ;
61876186 let indent = start - Math . max ( content . lastIndexOf ( "\n" , start ) , 0 ) + 4 ;
6188- let text : string ;
6187+ function pushComment ( text : string ) {
6188+ if ( ! margin ) {
6189+ margin = indent ;
6190+ }
6191+ comments . push ( text ) ;
6192+ indent += text . length ;
6193+ }
61896194
61906195 nextJSDocToken ( ) ;
61916196 while ( token ( ) === SyntaxKind . WhitespaceTrivia ) {
61926197 nextJSDocToken ( ) ;
61936198 }
61946199 if ( token ( ) === SyntaxKind . NewLineTrivia ) {
6195- canParseTag = true ;
6196- seenAsterisk = false ;
6200+ state = JSDocState . BeginningOfLine ;
61976201 nextJSDocToken ( ) ;
61986202 }
61996203 while ( token ( ) !== SyntaxKind . EndOfFileToken ) {
62006204 switch ( token ( ) ) {
62016205 case SyntaxKind . AtToken :
6202- if ( canParseTag ) {
6206+ if ( state === JSDocState . BeginningOfLine || state === JSDocState . SawAsterisk ) {
62036207 removeTrailingNewlines ( comments ) ;
62046208 parseTag ( indent ) ;
6205- // This will take us past the end of the line, so it's OK to parse a tag on the next pass through the loop
6206- // NOTE: According to usejsdoc.org, a tag goes to end of line, except the last tag. But real-world comments may break this rule.
6207- seenAsterisk = false ;
6209+ // NOTE: According to usejsdoc.org, a tag goes to end of line, except the last tag.
6210+ // Real-world comments may break this rule, so "BeginningOfLine" will not be a real line beginning
6211+ // for malformed examples like `/** @param {string } x @returns {number } the length */`
6212+ state = JSDocState . BeginningOfLine ;
62086213 advanceToken = false ;
62096214 margin = undefined ;
6215+ indent ++ ;
62106216 }
62116217 else {
6212- comments . push ( scanner . getTokenText ( ) ) ;
6218+ pushComment ( scanner . getTokenText ( ) ) ;
62136219 }
6214- indent ++ ;
62156220 break ;
6216-
62176221 case SyntaxKind . NewLineTrivia :
6218- // After a line break, we can parse a tag, and we haven't seen an asterisk on the next line yet
62196222 comments . push ( scanner . getTokenText ( ) ) ;
6220- canParseTag = true ;
6221- seenAsterisk = false ;
6223+ state = JSDocState . BeginningOfLine ;
62226224 indent = 0 ;
62236225 break ;
6224-
62256226 case SyntaxKind . AsteriskToken :
6226- text = scanner . getTokenText ( ) ;
6227- if ( seenAsterisk ) {
6227+ const asterisk = scanner . getTokenText ( ) ;
6228+ if ( state === JSDocState . SawAsterisk ) {
62286229 // If we've already seen an asterisk, then we can no longer parse a tag on this line
6229- canParseTag = false ;
6230- comments . push ( text ) ;
6231- if ( ! margin ) {
6232- margin = indent ;
6233- }
6230+ state = JSDocState . SavingComments ;
6231+ pushComment ( asterisk ) ;
6232+ }
6233+ else {
6234+ // Ignore the first asterisk on a line
6235+ state = JSDocState . SawAsterisk ;
6236+ indent += asterisk . length ;
62346237 }
6235- // Ignore the first asterisk on a line
6236- seenAsterisk = true ;
6237- indent += text . length ;
62386238 break ;
6239-
62406239 case SyntaxKind . Identifier :
62416240 // Anything else is doc comment text. We just save it. Because it
62426241 // wasn't a tag, we can no longer parse a tag on this line until we hit the next
62436242 // line break.
6244- text = scanner . getTokenText ( ) ;
6245- comments . push ( text ) ;
6246- if ( ! margin ) {
6247- margin = indent ;
6248- }
6249- canParseTag = false ;
6250- indent += text . length ;
6243+ pushComment ( scanner . getTokenText ( ) ) ;
6244+ state = JSDocState . SavingComments ;
62516245 break ;
6252-
6253-
62546246 case SyntaxKind . WhitespaceTrivia :
6255- // only collect whitespace if we *know* that we 're just looking at comments, not a possible jsdoc tag
6256- text = scanner . getTokenText ( ) ;
6257- if ( ! canParseTag || margin !== undefined && indent + text . length > margin ) {
6258- comments . push ( text . slice ( margin - indent - 1 ) ) ;
6247+ // only collect whitespace if we're already saving comments or have just crossed the comment indent margin
6248+ const whitespace = scanner . getTokenText ( ) ;
6249+ if ( state === JSDocState . SavingComments || margin !== undefined && indent + whitespace . length > margin ) {
6250+ comments . push ( whitespace . slice ( margin - indent - 1 ) ) ;
62596251 }
6260- indent += text . length ;
6252+ indent += whitespace . length ;
62616253 break ;
6262-
62636254 case SyntaxKind . EndOfFileToken :
62646255 break ;
6265-
62666256 default :
6267- text = scanner . getTokenText ( ) ;
6268- comments . push ( text ) ;
6269- indent += text . length ;
6257+ pushComment ( scanner . getTokenText ( ) ) ;
62706258 break ;
62716259 }
62726260 if ( advanceToken ) {
@@ -6365,58 +6353,58 @@ namespace ts {
63656353
63666354 function parseTagComments ( indent : number ) {
63676355 const comments : string [ ] = [ ] ;
6368- let state = TagState . SawAsterisk ;
6369- let done = false ;
6356+ let state = JSDocState . SawAsterisk ;
63706357 let margin : number | undefined ;
6371- let text : string ;
63726358 function pushComment ( text : string ) {
63736359 if ( ! margin ) {
63746360 margin = indent ;
63756361 }
63766362 comments . push ( text ) ;
63776363 indent += text . length ;
63786364 }
6379- while ( ! done && token ( ) !== SyntaxKind . EndOfFileToken ) {
6380- text = scanner . getTokenText ( ) ;
6365+ while ( token ( ) !== SyntaxKind . AtToken && token ( ) !== SyntaxKind . EndOfFileToken ) {
63816366 switch ( token ( ) ) {
63826367 case SyntaxKind . NewLineTrivia :
6383- if ( state >= TagState . SawAsterisk ) {
6384- state = TagState . BeginningOfLine ;
6385- comments . push ( text ) ;
6368+ if ( state >= JSDocState . SawAsterisk ) {
6369+ state = JSDocState . BeginningOfLine ;
6370+ comments . push ( scanner . getTokenText ( ) ) ;
63866371 }
63876372 indent = 0 ;
63886373 break ;
63896374 case SyntaxKind . AtToken :
6390- done = true ;
6375+ // Done
63916376 break ;
63926377 case SyntaxKind . WhitespaceTrivia :
6393- if ( state === TagState . SavingComments ) {
6394- pushComment ( text ) ;
6378+ if ( state === JSDocState . SavingComments ) {
6379+ pushComment ( scanner . getTokenText ( ) ) ;
63956380 }
63966381 else {
6382+ const whitespace = scanner . getTokenText ( ) ;
63976383 // if the whitespace crosses the margin, take only the whitespace that passes the margin
6398- if ( margin !== undefined && indent + text . length > margin ) {
6399- comments . push ( text . slice ( margin - indent - 1 ) ) ;
6384+ if ( margin !== undefined && indent + whitespace . length > margin ) {
6385+ comments . push ( whitespace . slice ( margin - indent - 1 ) ) ;
64006386 }
6401- indent += text . length ;
6387+ indent += whitespace . length ;
64026388 }
64036389 break ;
64046390 case SyntaxKind . AsteriskToken :
6405- if ( state === TagState . BeginningOfLine ) {
6391+ if ( state === JSDocState . BeginningOfLine ) {
64066392 // leading asterisks start recording on the *next* (non-whitespace) token
6407- state = TagState . SawAsterisk ;
6408- indent += text . length ;
6393+ state = JSDocState . SawAsterisk ;
6394+ indent += scanner . getTokenText ( ) . length ;
64096395 break ;
64106396 }
64116397 // FALLTHROUGH otherwise to record the * as a comment
64126398 default :
6413- state = TagState . SavingComments ; // leading identifiers start recording as well
6414- pushComment ( text ) ;
6399+ state = JSDocState . SavingComments ; // leading identifiers start recording as well
6400+ pushComment ( scanner . getTokenText ( ) ) ;
64156401 break ;
64166402 }
6417- if ( ! done ) {
6418- nextJSDocToken ( ) ;
6403+ if ( token ( ) === SyntaxKind . AtToken ) {
6404+ // Done
6405+ break ;
64196406 }
6407+ nextJSDocToken ( ) ;
64206408 }
64216409
64226410 removeLeadingNewlines ( comments ) ;
0 commit comments