@@ -233,7 +233,8 @@ class SphinxDocsGenerator extends Generator {
233233 this . namespaceStack . push ( { name : className , underClass : true } ) ;
234234 }
235235
236- protected onEndClass ( _cls : spec . ClassType ) {
236+ protected onEndClass ( cls : spec . ClassType ) {
237+ this . renderInheritedMembers ( cls ) ;
237238 this . code . closeBlock ( ) ;
238239 this . namespaceStack . pop ( ) ;
239240 if ( ! this . topNamespace . underClass ) { this . closeSection ( ) ; }
@@ -342,7 +343,8 @@ class SphinxDocsGenerator extends Generator {
342343 this . code . line ( ) ;
343344 }
344345
345- protected onEndInterface ( _ifc : spec . InterfaceType ) {
346+ protected onEndInterface ( ifc : spec . InterfaceType ) {
347+ this . renderInheritedMembers ( ifc ) ;
346348 this . code . closeBlock ( ) ;
347349 if ( ! this . topNamespace . underClass ) { this . closeSection ( ) ; }
348350 }
@@ -359,6 +361,62 @@ class SphinxDocsGenerator extends Generator {
359361 this . renderProperty ( property ) ;
360362 }
361363
364+ private renderInheritedMembers ( entity : spec . ClassType | spec . InterfaceType ) {
365+ const inherited = this . getInheritedMembers ( entity ) ;
366+ if ( Object . keys ( inherited ) . length === 0 ) { return ; }
367+ for ( const source of Object . keys ( inherited ) . sort ( ) ) {
368+ const entities = inherited [ source ] ;
369+ for ( const method of entities . methods ) {
370+ this . renderMethod ( method , source ) ;
371+ for ( const overload of this . createOverloadsForOptionals ( method ) ) {
372+ this . renderMethod ( overload , source ) ;
373+ }
374+ }
375+ for ( const property of entities . properties ) {
376+ this . renderProperty ( property , source ) ;
377+ }
378+ }
379+ }
380+
381+ private getInheritedMembers ( entity : spec . ClassType | spec . InterfaceType ) : InheritedMembers {
382+ const parents = parentTypes ( entity ) ;
383+ const knownMembers = new Set < string > ( [
384+ ...( entity . methods || [ ] ) . map ( m => m . name ! ) ,
385+ ...( entity . properties || [ ] ) . map ( p => p . name )
386+ ] ) ;
387+ const result : InheritedMembers = { } ;
388+ for ( const parent of parents ) {
389+ const parentType = this . findType ( parent . fqn ) as spec . ClassType | spec . InterfaceType ;
390+ for ( const method of parentType . methods || [ ] ) {
391+ if ( method . static || knownMembers . has ( method . name ! ) ) { continue ; }
392+ result [ parentType . fqn ] = result [ parentType . fqn ] || { methods : [ ] , properties : [ ] } ;
393+ result [ parentType . fqn ] . methods . push ( method ) ;
394+ knownMembers . add ( method . name ! ) ;
395+ }
396+ for ( const property of parentType . properties || [ ] ) {
397+ if ( property . static || knownMembers . has ( property . name ! ) ) { continue ; }
398+ result [ parentType . fqn ] = result [ parentType . fqn ] || { methods : [ ] , properties : [ ] } ;
399+ result [ parentType . fqn ] . properties . push ( property ) ;
400+ knownMembers . add ( property . name ) ;
401+ }
402+ for ( const superType of parentTypes ( parentType ) ) {
403+ parents . push ( superType ) ;
404+ }
405+ }
406+ return result ;
407+
408+ function parentTypes ( type : spec . ClassType | spec . InterfaceType ) {
409+ const types = new Array < spec . NamedTypeReference > ( ) ;
410+ if ( spec . isClassType ( type ) && type . base ) {
411+ types . push ( type . base ) ;
412+ }
413+ if ( type . interfaces ) {
414+ types . push ( ...type . interfaces ) ;
415+ }
416+ return types ;
417+ }
418+ }
419+
362420 /**
363421 * Adds a title to the current code file, using the appropriate header
364422 * adornment given the current TOC depth. It will start using simple
@@ -402,7 +460,7 @@ class SphinxDocsGenerator extends Generator {
402460 signature += ', ' ;
403461 }
404462
405- if ( p . type . optional ) {
463+ if ( p . type . optional && ! params . slice ( idx + 1 ) . find ( e => ! e . type . optional ) ) {
406464 signature += '[' ;
407465 signaturePosfix += ']' ;
408466 }
@@ -437,22 +495,40 @@ class SphinxDocsGenerator extends Generator {
437495 }
438496 }
439497
440- private renderMethod ( method : spec . Method ) {
498+ private renderMethod ( method : spec . Method , inheritedFrom ?: string ) {
441499 const signature = this . renderMethodSignature ( method ) ;
442500
443501 const type = method . static ? `py:staticmethod` : `py:method` ;
444502
445503 this . code . line ( ) ;
446504 this . code . openBlock ( `.. ${ type } :: ${ method . name } ${ signature } ` ) ;
447505
506+ if ( inheritedFrom ) {
507+ this . code . line ( ) ;
508+ this . code . line ( `*Inherited from* :py:meth:\`${ inheritedFrom } <${ inheritedFrom } .${ method . name } >\`` ) ;
509+ } else if ( method . overrides ) {
510+ this . code . line ( ) ;
511+ const superType = this . findType ( method . overrides . fqn ) as spec . ClassType | spec . InterfaceType ;
512+ if ( spec . isInterfaceType ( superType ) || superType . methods ! . find ( m => m . name === method . name && ! ! m . abstract ) ) {
513+ this . code . line ( `*Implements* :py:meth:\`${ method . overrides . fqn } .${ method . name } \`` ) ;
514+ } else {
515+ this . code . line ( `*Overrides* :py:meth:\`${ method . overrides . fqn } .${ method . name } \`` ) ;
516+ }
517+ }
448518 this . renderDocsLine ( method ) ;
449519 this . code . line ( ) ;
520+ if ( method . protected ) {
521+ this . code . line ( '*Protected method*' ) ;
522+ this . code . line ( ) ;
523+ }
450524
451525 this . renderMethodParameters ( method ) ;
452526
453527 // @return doc
454528 if ( method . docs && method . docs . return ) {
455- this . code . line ( `:return: ${ method . docs . return } ` ) ;
529+ const [ firstLine , ...rest ] = method . docs . return . split ( '\n' ) ;
530+ this . code . line ( `:return: ${ firstLine } ` ) ;
531+ rest . forEach ( line => this . code . line ( ` ${ line } ` ) ) ;
456532 }
457533
458534 if ( method . returns ) {
@@ -542,7 +618,7 @@ class SphinxDocsGenerator extends Generator {
542618 } else {
543619 throw new Error ( 'Unexpected type ref' ) ;
544620 }
545- if ( type . optional ) { result . ref = `${ result . ref } or undefined` ; }
621+ if ( type . optional ) { result . ref = `${ result . ref } or \`\` undefined\`\` ` ; }
546622 return result ;
547623
548624 // Wrap a string between parenthesis if it contains " or "
@@ -552,12 +628,28 @@ class SphinxDocsGenerator extends Generator {
552628 }
553629 }
554630
555- private renderProperty ( prop : spec . Property ) {
631+ private renderProperty ( prop : spec . Property , inheritedFrom ?: string ) {
556632 this . code . line ( ) ;
557633 const type = this . renderTypeRef ( prop . type ) ;
558634 this . code . openBlock ( `.. py:attribute:: ${ prop . name } ` ) ;
635+ if ( inheritedFrom ) {
636+ this . code . line ( ) ;
637+ this . code . line ( `*Inherited from* :py:attr:\`${ inheritedFrom } <${ inheritedFrom } .${ prop . name } >\`` ) ;
638+ } else if ( prop . overrides ) {
639+ this . code . line ( ) ;
640+ const superType = this . findType ( prop . overrides . fqn ) as spec . ClassType | spec . InterfaceType ;
641+ if ( spec . isInterfaceType ( superType ) || superType . properties ! . find ( p => p . name === prop . name && ! ! p . abstract ) ) {
642+ this . code . line ( `*Implements* :py:meth:\`${ prop . overrides . fqn } .${ prop . name } \`` ) ;
643+ } else {
644+ this . code . line ( `*Overrides* :py:attr:\`${ prop . overrides . fqn } .${ prop . name } \`` ) ;
645+ }
646+ }
559647 this . renderDocsLine ( prop ) ;
560648 this . code . line ( ) ;
649+ if ( prop . protected ) {
650+ this . code . line ( '*Protected property*' ) ;
651+ this . code . line ( ) ;
652+ }
561653 const readonly = prop . immutable ? ' *(readonly)*' : '' ;
562654 const abs = prop . abstract ? ' *(abstract)*' : '' ;
563655 const stat = prop . static ? ' *(static)*' : '' ;
@@ -665,3 +757,5 @@ function formatLanguage(language: string): string {
665757 return language ;
666758 }
667759}
760+
761+ type InheritedMembers = { [ typeFqn : string ] : { methods : spec . Method [ ] , properties : spec . Property [ ] } } ;
0 commit comments