@@ -21,6 +21,34 @@ export function findNodesByType(node: Node, type: string): Node[] {
2121 return results
2222}
2323
24+ function stripDocstring ( raw : string ) : string {
25+ let content : string
26+ if (
27+ ( raw . startsWith ( '"""' ) && raw . endsWith ( '"""' ) ) ||
28+ ( raw . startsWith ( "'''" ) && raw . endsWith ( "'''" ) )
29+ ) {
30+ content = raw . slice ( 3 , - 3 )
31+ } else {
32+ content = raw . slice ( 1 , - 1 )
33+ }
34+
35+ // Dedent: strip common leading whitespace (like Python's textwrap.dedent)
36+ const lines = content . split ( "\n" )
37+ // First line is either empty or unindented (follows opening quotes), so skip it
38+ const indentedLines = lines . slice ( 1 ) . filter ( ( l ) => l . trim ( ) . length > 0 )
39+ if ( indentedLines . length === 0 ) {
40+ return content . trim ( )
41+ }
42+
43+ // Find minimum indentation of all non-empty lines (except first) so we can
44+ // remove it from all lines, preserving relative indentation
45+ const minIndent = Math . min (
46+ ...indentedLines . map ( ( l ) => l . length - l . trimStart ( ) . length ) ,
47+ )
48+ const dedented = lines . map ( ( l , i ) => ( i === 0 ? l : l . slice ( minIndent ) ) )
49+ return dedented . join ( "\n" ) . trim ( )
50+ }
51+
2452function collectNodesByType ( node : Node , type : string , results : Node [ ] ) : void {
2553 if ( node . type === type ) {
2654 results . push ( node )
@@ -179,14 +207,23 @@ export function decoratorExtractor(node: Node): RouteInfo | null {
179207 // Grammar guarantees: decorated_definition always has a definition field with a name
180208 const functionDefNode = node . childForFieldName ( "definition" ) !
181209 const functionName = functionDefNode . childForFieldName ( "name" ) ?. text ?? ""
182-
210+ const functionBody = functionDefNode . childForFieldName ( "body" )
211+ const firstStatement = functionBody ?. namedChildren [ 0 ]
212+ let docstring : string | undefined
213+ if ( firstStatement ?. type === "expression_statement" ) {
214+ const expr = firstStatement . firstNamedChild
215+ if ( expr ?. type === "string" ) {
216+ docstring = stripDocstring ( expr . text )
217+ }
218+ }
183219 return {
184220 owner : objectNode . text ,
185221 method : resolvedMethod ,
186222 path,
187223 function : functionName ,
188224 line : node . startPosition . row + 1 ,
189225 column : node . startPosition . column ,
226+ docstring,
190227 }
191228}
192229
0 commit comments