11/* @internal */
22namespace ts . JsDoc {
3+ const singleLineTemplate = { newText : "/** */" , caretOffset : 3 } ;
34 const jsDocTagNames = [
45 "augments" ,
56 "author" ,
@@ -195,13 +196,9 @@ namespace ts.JsDoc {
195196 /**
196197 * Checks if position points to a valid position to add JSDoc comments, and if so,
197198 * returns the appropriate template. Otherwise returns an empty string.
198- * Valid positions are
199- * - outside of comments, statements, and expressions, and
200- * - preceding a:
201- * - function/constructor/method declaration
202- * - class declarations
203- * - variable statements
204- * - namespace declarations
199+ * Invalid positions are
200+ * - within comments, strings (including template literals and regex), and JSXText
201+ * - within a token
205202 *
206203 * Hosts should ideally check that:
207204 * - The line is all whitespace up to 'position' before performing the insertion.
@@ -212,7 +209,8 @@ namespace ts.JsDoc {
212209 * @param position The (character-indexed) position in the file where the check should
213210 * be performed.
214211 */
215- export function getDocCommentTemplateAtPosition ( newLine : string , sourceFile : SourceFile , position : number ) : TextInsertion {
212+
213+ export function getDocCommentTemplateAtPosition ( newLine : string , sourceFile : SourceFile , position : number ) : TextInsertion | undefined {
216214 // Check if in a context where we don't want to perform any insertion
217215 if ( isInString ( sourceFile , position ) || isInComment ( sourceFile , position ) || hasDocComment ( sourceFile , position ) ) {
218216 return undefined ;
@@ -226,35 +224,33 @@ namespace ts.JsDoc {
226224
227225 const commentOwnerInfo = getCommentOwnerInfo ( tokenAtPos ) ;
228226 if ( ! commentOwnerInfo ) {
229- return undefined ;
227+ // if climbing the tree did not find a declaration with parameters, complete to a single line comment
228+ return singleLineTemplate ;
230229 }
231230 const { commentOwner, parameters } = commentOwnerInfo ;
232- if ( commentOwner . getStart ( ) < position ) {
231+
232+ if ( commentOwner . kind === SyntaxKind . JsxText ) {
233233 return undefined ;
234234 }
235235
236+ if ( commentOwner . getStart ( ) < position || parameters . length === 0 ) {
237+ // if climbing the tree found a declaration with parameters but the request was made inside it
238+ // or if there are no parameters, complete to a single line comment
239+ return singleLineTemplate ;
240+ }
241+
236242 const posLineAndChar = sourceFile . getLineAndCharacterOfPosition ( position ) ;
237243 const lineStart = sourceFile . getLineStarts ( ) [ posLineAndChar . line ] ;
238244
239245 // replace non-whitespace characters in prefix with spaces.
240246 const indentationStr = sourceFile . text . substr ( lineStart , posLineAndChar . character ) . replace ( / \S / i, ( ) => " " ) ;
241247 const isJavaScriptFile = hasJavaScriptFileExtension ( sourceFile . fileName ) ;
242248
243- let docParams = "" ;
244- if ( parameters ) {
245- for ( let i = 0 ; i < parameters . length ; i ++ ) {
246- const currentName = parameters [ i ] . name ;
247- const paramName = currentName . kind === SyntaxKind . Identifier ?
248- ( < Identifier > currentName ) . escapedText :
249- "param" + i ;
250- if ( isJavaScriptFile ) {
251- docParams += `${ indentationStr } * @param {any} ${ paramName } ${ newLine } ` ;
252- }
253- else {
254- docParams += `${ indentationStr } * @param ${ paramName } ${ newLine } ` ;
255- }
256- }
257- }
249+ const docParams = parameters . map ( ( { name} , i ) => {
250+ const nameText = isIdentifier ( name ) ? name . text : `param${ i } ` ;
251+ const type = isJavaScriptFile ? "{any} " : "" ;
252+ return `${ indentationStr } * @param ${ type } ${ nameText } ${ newLine } ` ;
253+ } ) . join ( "" ) ;
258254
259255 // A doc comment consists of the following
260256 // * The opening comment line
@@ -276,43 +272,30 @@ namespace ts.JsDoc {
276272
277273 interface CommentOwnerInfo {
278274 readonly commentOwner : Node ;
279- readonly parameters ? : ReadonlyArray < ParameterDeclaration > ;
275+ readonly parameters : ReadonlyArray < ParameterDeclaration > ;
280276 }
281277 function getCommentOwnerInfo ( tokenAtPos : Node ) : CommentOwnerInfo | undefined {
282- // TODO: add support for:
283- // - enums/enum members
284- // - interfaces
285- // - property declarations
286- // - potentially property assignments
287278 for ( let commentOwner = tokenAtPos ; commentOwner ; commentOwner = commentOwner . parent ) {
288279 switch ( commentOwner . kind ) {
289280 case SyntaxKind . FunctionDeclaration :
290281 case SyntaxKind . MethodDeclaration :
291282 case SyntaxKind . Constructor :
292- const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration ;
283+ case SyntaxKind . MethodSignature :
284+ const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature ;
293285 return { commentOwner, parameters } ;
294286
295- case SyntaxKind . ClassDeclaration :
296- return { commentOwner } ;
297-
298287 case SyntaxKind . VariableStatement : {
299288 const varStatement = < VariableStatement > commentOwner ;
300289 const varDeclarations = varStatement . declarationList . declarations ;
301290 const parameters = varDeclarations . length === 1 && varDeclarations [ 0 ] . initializer
302291 ? getParametersFromRightHandSideOfAssignment ( varDeclarations [ 0 ] . initializer )
303292 : undefined ;
304- return { commentOwner, parameters } ;
293+ return parameters ? { commentOwner, parameters } : undefined ;
305294 }
306295
307296 case SyntaxKind . SourceFile :
308297 return undefined ;
309298
310- case SyntaxKind . ModuleDeclaration :
311- // If in walking up the tree, we hit a a nested namespace declaration,
312- // then we must be somewhere within a dotted namespace name; however we don't
313- // want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
314- return commentOwner . parent . kind === SyntaxKind . ModuleDeclaration ? undefined : { commentOwner } ;
315-
316299 case SyntaxKind . BinaryExpression : {
317300 const be = commentOwner as BinaryExpression ;
318301 if ( getSpecialPropertyAssignmentKind ( be ) === ts . SpecialPropertyAssignmentKind . None ) {
@@ -321,6 +304,11 @@ namespace ts.JsDoc {
321304 const parameters = isFunctionLike ( be . right ) ? be . right . parameters : emptyArray ;
322305 return { commentOwner, parameters } ;
323306 }
307+
308+ case SyntaxKind . JsxText : {
309+ const parameters : ReadonlyArray < ParameterDeclaration > = emptyArray ;
310+ return { commentOwner, parameters } ;
311+ }
324312 }
325313 }
326314 }
0 commit comments