1+ """Renderer for Fern."""
2+
3+ from __future__ import annotations
4+
5+ import re
6+ import typing as t
7+
8+ from autodoc2 .render .base import RendererBase
9+
10+ if t .TYPE_CHECKING :
11+ from autodoc2 .utils import ItemData
12+
13+
14+ _RE_DELIMS = re .compile (r"(\s*[\[\]\(\),]\s*)" )
15+
16+
17+ class FernRenderer (RendererBase ):
18+ """Render the documentation as Fern-compatible Markdown."""
19+
20+ EXTENSION = ".md"
21+
22+ def render_item (self , full_name : str ) -> t .Iterable [str ]:
23+ """Render a single item by dispatching to the appropriate method."""
24+ item = self .get_item (full_name )
25+ if item is None :
26+ raise ValueError (f"Item { full_name } does not exist" )
27+
28+ type_ = item ["type" ]
29+ if type_ == "package" :
30+ yield from self .render_package (item )
31+ elif type_ == "module" :
32+ yield from self .render_module (item )
33+ elif type_ == "function" :
34+ yield from self .render_function (item )
35+ elif type_ == "class" :
36+ yield from self .render_class (item )
37+ elif type_ == "exception" :
38+ yield from self .render_exception (item )
39+ elif type_ == "property" :
40+ yield from self .render_property (item )
41+ elif type_ == "method" :
42+ yield from self .render_method (item )
43+ elif type_ == "attribute" :
44+ yield from self .render_attribute (item )
45+ elif type_ == "data" :
46+ yield from self .render_data (item )
47+ else :
48+ self .warn (f"Unknown item type { type_ !r} for { full_name !r} " )
49+
50+ def render_function (self , item : ItemData ) -> t .Iterable [str ]:
51+ """Create the content for a function."""
52+ short_name = item ["full_name" ].split ("." )[- 1 ]
53+ show_annotations = self .show_annotations (item )
54+
55+ # Build signature
56+ sig = f"{ short_name } ({ self .format_args (item ['args' ], show_annotations )} )"
57+ if show_annotations and item .get ("return_annotation" ):
58+ sig += f" -> { self .format_annotation (item ['return_annotation' ])} "
59+
60+ # Fern-style output (starting simple)
61+ yield f"## { sig } "
62+ yield ""
63+
64+ if self .show_docstring (item ):
65+ yield item ['doc' ] # Direct docstring - no directives!
66+ yield ""
67+
68+ def render_module (self , item : ItemData ) -> t .Iterable [str ]:
69+ """Create the content for a module."""
70+ # For now, delegate to package rendering
71+ yield from self .render_package (item )
72+
73+ def render_package (self , item : ItemData ) -> t .Iterable [str ]:
74+ """Create the content for a package."""
75+ full_name = item ["full_name" ]
76+ yield f"# { full_name } "
77+ yield ""
78+
79+ if self .show_docstring (item ):
80+ yield item ['doc' ]
81+ yield ""
82+
83+ # Get visible children
84+ visible_children = [
85+ i ["full_name" ]
86+ for i in self .get_children (item )
87+ if i ["type" ] not in ("package" , "module" )
88+ ]
89+
90+ for name in visible_children :
91+ yield from self .render_item (name )
92+
93+ # Placeholder methods - we'll implement these later
94+ def render_class (self , item : ItemData ) -> t .Iterable [str ]:
95+ """Create the content for a class."""
96+ yield f"## { item ['full_name' ].split ('.' )[- 1 ]} (class)"
97+ yield "TODO: Implement class rendering"
98+ yield ""
99+
100+ def render_exception (self , item : ItemData ) -> t .Iterable [str ]:
101+ """Create the content for an exception."""
102+ yield from self .render_class (item )
103+
104+ def render_property (self , item : ItemData ) -> t .Iterable [str ]:
105+ """Create the content for a property."""
106+ yield f"### { item ['full_name' ].split ('.' )[- 1 ]} (property)"
107+ yield "TODO: Implement property rendering"
108+ yield ""
109+
110+ def render_method (self , item : ItemData ) -> t .Iterable [str ]:
111+ """Create the content for a method."""
112+ yield from self .render_function (item ) # Same as function for now
113+
114+ def render_attribute (self , item : ItemData ) -> t .Iterable [str ]:
115+ """Create the content for an attribute."""
116+ yield from self .render_data (item )
117+
118+ def render_data (self , item : ItemData ) -> t .Iterable [str ]:
119+ """Create the content for a data item."""
120+ short_name = item ["full_name" ].split ("." )[- 1 ]
121+ yield f"### { short_name } "
122+
123+ if item .get ("annotation" ):
124+ yield f"**Type**: `{ self .format_annotation (item ['annotation' ])} `"
125+
126+ value = item .get ("value" )
127+ if value is not None :
128+ yield f"**Value**: `{ value } `"
129+
130+ if self .show_docstring (item ):
131+ yield ""
132+ yield item ['doc' ]
133+ yield ""
134+
135+ def generate_summary (
136+ self , objects : list [ItemData ], alias : dict [str , str ] | None = None
137+ ) -> t .Iterable [str ]:
138+ """Generate a summary of the objects."""
139+ alias = alias or {}
140+
141+ yield "| Name | Description |"
142+ yield "|------|-------------|"
143+
144+ for item in objects :
145+ full_name = item ["full_name" ]
146+ display_name = alias .get (full_name , full_name .split ("." )[- 1 ])
147+
148+ # Get first line of docstring for description
149+ doc = item .get ('doc' , '' ).strip ()
150+ description = doc .split ('\n ' )[0 ] if doc else ""
151+ if len (description ) > 50 :
152+ description = description [:47 ] + "..."
153+
154+ yield f"| `{ display_name } ` | { description } |"
0 commit comments