or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdconfiguration.mdcore-parsing.mdindex.mdlink-processing.mdrendering.mdsyntax-tree.mdtoken-system.md

rendering.mddocs/

0

# Rendering and Output

1

2

HTML rendering system with customizable render rules and support for custom renderers to generate different output formats from parsed markdown tokens.

3

4

## Capabilities

5

6

### HTML Renderer

7

8

Default renderer that converts tokens to HTML output.

9

10

```python { .api }

11

class RendererHTML:

12

"""HTML renderer with customizable render rules."""

13

14

__output__ = "html" # Output format identifier

15

16

def __init__(self, parser: MarkdownIt = None):

17

"""

18

Initialize HTML renderer.

19

20

Parameters:

21

- parser: MarkdownIt instance (for reference)

22

"""

23

24

def render(self, tokens: list[Token], options: dict, env: dict) -> str:

25

"""

26

Render token stream to HTML.

27

28

Parameters:

29

- tokens: list of tokens to render

30

- options: parser options

31

- env: environment data

32

33

Returns:

34

- str: rendered HTML output

35

"""

36

37

def renderInline(self, tokens: list[Token], options: dict, env: dict) -> str:

38

"""

39

Render inline tokens to HTML.

40

41

Parameters:

42

- tokens: list of inline tokens

43

- options: parser options

44

- env: environment data

45

46

Returns:

47

- str: rendered HTML without block wrappers

48

"""

49

50

def renderToken(self, tokens: list[Token], idx: int, options: dict, env: dict) -> str:

51

"""

52

Render single token at specified index.

53

54

Parameters:

55

- tokens: full token list

56

- idx: index of token to render

57

- options: parser options

58

- env: environment data

59

60

Returns:

61

- str: rendered HTML for single token

62

"""

63

```

64

65

**Usage Example:**

66

67

```python

68

from markdown_it import MarkdownIt

69

from markdown_it.renderer import RendererHTML

70

71

md = MarkdownIt()

72

tokens = md.parse("# Hello\n\n**Bold** text.")

73

74

# Direct rendering

75

html = md.renderer.render(tokens, md.options, {})

76

print(html)

77

78

# Custom renderer instance

79

custom_renderer = RendererHTML()

80

html = custom_renderer.render(tokens, md.options, {})

81

```

82

83

### Custom Render Rules

84

85

Customize rendering for specific token types.

86

87

```python { .api }

88

# Render rules dictionary

89

rules: dict[str, callable] # Maps token types to render functions

90

91

def add_render_rule(self, name: str, function: callable, fmt: str = "html") -> None:

92

"""

93

Add custom render rule for token type.

94

95

Parameters:

96

- name: token type name

97

- function: render function with signature (tokens, idx, options, env) -> str

98

- fmt: output format (must match renderer.__output__)

99

"""

100

```

101

102

**Usage Example:**

103

104

```python

105

from markdown_it import MarkdownIt

106

107

def custom_heading_rule(tokens, idx, options, env):

108

"""Custom heading renderer with anchor links."""

109

token = tokens[idx]

110

if token.nesting == 1: # opening tag

111

# Get heading text from next inline token

112

heading_text = tokens[idx + 1].content if idx + 1 < len(tokens) else ""

113

anchor_id = heading_text.lower().replace(" ", "-")

114

return f'<{token.tag} id="{anchor_id}">'

115

else: # closing tag

116

return f'</{token.tag}>'

117

118

md = MarkdownIt()

119

md.add_render_rule("heading_open", custom_heading_rule)

120

md.add_render_rule("heading_close", custom_heading_rule)

121

122

html = md.render("# Hello World")

123

# Output: <h1 id="hello-world">Hello World</h1>

124

```

125

126

### Custom Renderer Classes

127

128

Create custom renderers for different output formats.

129

130

```python { .api }

131

class RendererProtocol(Protocol):

132

"""Protocol defining renderer interface."""

133

134

__output__: str # Output format identifier

135

136

def render(self, tokens: list[Token], options: dict, env: dict) -> Any:

137

"""Render tokens to output format."""

138

```

139

140

**Usage Example:**

141

142

```python

143

from markdown_it import MarkdownIt

144

from markdown_it.renderer import RendererProtocol

145

146

class MarkdownRenderer(RendererProtocol):

147

"""Custom renderer that outputs markdown (round-trip)."""

148

149

__output__ = "markdown"

150

151

def __init__(self, parser=None):

152

self.rules = {}

153

154

def render(self, tokens, options, env):

155

result = ""

156

for i, token in enumerate(tokens):

157

if token.type in self.rules:

158

result += self.rules[token.type](tokens, i, options, env)

159

else:

160

result += self.default_render(token)

161

return result

162

163

def default_render(self, token):

164

if token.type == "heading_open":

165

return "#" * int(token.tag[1]) + " "

166

elif token.type == "paragraph_open":

167

return ""

168

elif token.type == "inline":

169

return token.content

170

elif token.type in ["heading_close", "paragraph_close"]:

171

return "\n\n"

172

return ""

173

174

# Use custom renderer

175

md = MarkdownIt(renderer_cls=MarkdownRenderer)

176

markdown_output = md.render("# Title\n\nParagraph text.")

177

```

178

179

### Built-in Render Rules

180

181

Standard render rules for common token types:

182

183

```python { .api }

184

# Block elements

185

def code_block(self, tokens, idx, options, env): ...

186

def fence(self, tokens, idx, options, env): ...

187

def blockquote_open(self, tokens, idx, options, env): ...

188

def blockquote_close(self, tokens, idx, options, env): ...

189

def hr(self, tokens, idx, options, env): ...

190

191

# Lists

192

def bullet_list_open(self, tokens, idx, options, env): ...

193

def bullet_list_close(self, tokens, idx, options, env): ...

194

def list_item_open(self, tokens, idx, options, env): ...

195

def list_item_close(self, tokens, idx, options, env): ...

196

197

# Inline elements

198

def text(self, tokens, idx, options, env): ...

199

def html_inline(self, tokens, idx, options, env): ...

200

def html_block(self, tokens, idx, options, env): ...

201

def softbreak(self, tokens, idx, options, env): ...

202

def hardbreak(self, tokens, idx, options, env): ...

203

204

# Links and media

205

def link_open(self, tokens, idx, options, env): ...

206

def link_close(self, tokens, idx, options, env): ...

207

def image(self, tokens, idx, options, env): ...

208

209

# Tables

210

def table_open(self, tokens, idx, options, env): ...

211

def tr_open(self, tokens, idx, options, env): ...

212

def td_open(self, tokens, idx, options, env): ...

213

def th_open(self, tokens, idx, options, env): ...

214

```

215

216

### Render Rule Customization

217

218

Override built-in render rules for customization:

219

220

```python

221

from markdown_it import MarkdownIt

222

223

class CustomRenderer(RendererHTML):

224

"""Custom HTML renderer with modifications."""

225

226

def code_block(self, tokens, idx, options, env):

227

"""Custom code block rendering with line numbers."""

228

token = tokens[idx]

229

info = token.info.strip() if token.info else ""

230

lang = info.split()[0] if info else ""

231

232

code = token.content

233

lines = code.rstrip().split('\n')

234

235

html = f'<pre><code class="language-{lang}">'

236

for i, line in enumerate(lines, 1):

237

html += f'<span class="line-number">{i:3d}</span>{line}\n'

238

html += '</code></pre>'

239

240

return html

241

242

def strong_open(self, tokens, idx, options, env):

243

"""Use <b> instead of <strong>."""

244

return '<b>'

245

246

def strong_close(self, tokens, idx, options, env):

247

"""Use <b> instead of <strong>."""

248

return '</b>'

249

250

# Use custom renderer

251

md = MarkdownIt(renderer_cls=CustomRenderer)

252

html = md.render("```python\nprint('hello')\n```\n\n**Bold text**")

253

```

254

255

### Attribute Rendering

256

257

Utility methods for rendering HTML attributes:

258

259

```python { .api }

260

def renderAttrs(self, token: Token) -> str:

261

"""

262

Render token attributes to HTML attribute string.

263

264

Parameters:

265

- token: token with attributes

266

267

Returns:

268

- str: HTML attribute string

269

"""

270

271

def renderToken(self, tokens: list[Token], idx: int, options: dict, env: dict) -> str:

272

"""

273

Default token rendering with attribute support.

274

275

Parameters:

276

- tokens: token list

277

- idx: token index

278

- options: parser options

279

- env: environment data

280

281

Returns:

282

- str: rendered token HTML

283

"""

284

```

285

286

**Usage Example:**

287

288

```python

289

from markdown_it.token import Token

290

from markdown_it.renderer import RendererHTML

291

292

renderer = RendererHTML()

293

294

# Token with attributes

295

token = Token("div_open", "div", 1)

296

token.attrSet("class", "container")

297

token.attrSet("id", "main")

298

299

# Render attributes

300

attrs_html = renderer.renderAttrs(token)

301

print(attrs_html) # ' class="container" id="main"'

302

303

# Full token rendering

304

html = renderer.renderToken([token], 0, {}, {})

305

print(html) # '<div class="container" id="main">'

306

```

307

308

## Output Customization

309

310

### Escaping and Security

311

312

HTML escaping utilities for safe output:

313

314

```python { .api }

315

from markdown_it.common.utils import escapeHtml, unescapeAll

316

317

def escapeHtml(raw: str) -> str:

318

"""

319

Escape HTML characters for safe output.

320

321

Parameters:

322

- raw: raw text to escape

323

324

Returns:

325

- str: HTML-escaped text

326

"""

327

328

def unescapeAll(str: str) -> str:

329

"""

330

Unescape entities and backslash escapes.

331

332

Parameters:

333

- str: text to unescape

334

335

Returns:

336

- str: unescaped text

337

"""

338

```

339

340

### Link and URL Processing

341

342

URL handling in render context:

343

344

```python

345

def custom_link_render(tokens, idx, options, env):

346

"""Custom link rendering with security checks."""

347

token = tokens[idx]

348

349

if token.nesting == 1: # link_open

350

href = token.attrGet("href")

351

if href:

352

# Custom URL validation

353

if not is_safe_url(href):

354

return '<span class="invalid-link">'

355

# Add custom attributes

356

token.attrSet("rel", "noopener")

357

token.attrSet("target", "_blank")

358

359

# Render with attributes

360

return renderer.renderToken(tokens, idx, options, env)

361

else: # link_close

362

return '</a>' if token.attrGet("href") else '</span>'

363

364

md = MarkdownIt()

365

md.add_render_rule("link_open", custom_link_render)

366

md.add_render_rule("link_close", custom_link_render)

367

```

368

369

### Multiple Output Formats

370

371

Supporting multiple renderers:

372

373

```python

374

from markdown_it import MarkdownIt

375

376

class MultiFormatRenderer:

377

"""Wrapper supporting multiple output formats."""

378

379

def __init__(self):

380

self.html_renderer = RendererHTML()

381

self.markdown_renderer = MarkdownRenderer()

382

383

def render(self, tokens, options, env, format="html"):

384

if format == "html":

385

return self.html_renderer.render(tokens, options, env)

386

elif format == "markdown":

387

return self.markdown_renderer.render(tokens, options, env)

388

else:

389

raise ValueError(f"Unknown format: {format}")

390

391

# Usage

392

md = MarkdownIt()

393

tokens = md.parse("# Title\n\n**Bold** text.")

394

395

multi_renderer = MultiFormatRenderer()

396

html = multi_renderer.render(tokens, md.options, {}, "html")

397

markdown = multi_renderer.render(tokens, md.options, {}, "markdown")

398

```