0
# Document System
1
2
Low-level document creation and manipulation functions for building custom layout algorithms and pretty printers. The document system provides the foundation for prettyprinter's flexible and composable formatting capabilities.
3
4
## Capabilities
5
6
### Document Creation
7
8
Core functions for creating and combining document elements that represent formatted output structure.
9
10
```python { .api }
11
def concat(docs):
12
"""
13
Create document by concatenating a sequence of documents.
14
15
Parameters:
16
- docs: Iterable of documents (Doc instances or strings)
17
18
Returns:
19
- Doc: Concatenated document
20
21
Notes:
22
- Empty documents (NIL) are automatically filtered out
23
- String arguments are automatically converted to document format
24
"""
25
26
def group(doc):
27
"""
28
Create document that attempts single-line layout when possible.
29
30
Parameters:
31
- doc: Document to attempt to lay out on single line
32
33
Returns:
34
- Doc: Grouped document that tries flat layout first
35
36
Notes:
37
- Uses 'when_flat' branch of FlatChoice documents when fitting on one line
38
- Falls back to multiline layout when content doesn't fit
39
"""
40
41
def nest(i, doc):
42
"""
43
Create document with increased indentation level.
44
45
Parameters:
46
- i (int): Number of spaces to add to indentation
47
- doc: Document to indent
48
49
Returns:
50
- Doc: Document with increased indentation
51
"""
52
```
53
54
### Document Annotation
55
56
Functions for adding metadata and annotations to documents for syntax highlighting and other purposes.
57
58
```python { .api }
59
def annotate(annotation, doc):
60
"""
61
Annotate document with arbitrary metadata value.
62
63
Parameters:
64
- annotation: Arbitrary annotation value (often Token for syntax highlighting)
65
- doc: Document to annotate
66
67
Returns:
68
- Doc: Annotated document with metadata
69
70
Notes:
71
- Annotations are preserved through layout process
72
- Used extensively for syntax highlighting with Token values
73
"""
74
```
75
76
### Contextual Documents
77
78
Create documents that are evaluated lazily based on layout context, enabling adaptive formatting based on available space and current formatting state.
79
80
```python { .api }
81
def contextual(fn):
82
"""
83
Create document that is lazily evaluated during layout.
84
85
Parameters:
86
- fn: Function accepting (indent, column, page_width, ribbon_width) parameters
87
88
Returns:
89
- Doc: Contextual document evaluated during layout
90
91
Notes:
92
- Enables adaptive formatting based on current layout state
93
- Function is called during layout with current position and constraints
94
- Returned value from function must be a valid document
95
"""
96
```
97
98
### Advanced Layout Control
99
100
Functions providing fine-grained control over document layout behavior and line breaking.
101
102
```python { .api }
103
def always_break(doc):
104
"""
105
Create document that forces multiline layout.
106
107
Parameters:
108
- doc: Document to force to multiple lines
109
110
Returns:
111
- Doc: Document that will always break to multiple lines
112
113
Notes:
114
- Forces parent documents to also break to multiple lines
115
- Nested documents may still be laid out flat independently
116
"""
117
118
def flat_choice(when_broken, when_flat):
119
"""
120
Create document with conditional layout options.
121
122
Parameters:
123
- when_broken: Document used when parent is broken to multiple lines
124
- when_flat: Document used when parent fits on single line
125
126
Returns:
127
- Doc: Document with conditional layout behavior
128
129
Notes:
130
- Layout algorithm chooses appropriate branch based on fitting constraints
131
- Used internally by LINE, SOFTLINE constants
132
"""
133
134
def align(doc):
135
"""
136
Align each new line in document with the first new line.
137
138
Parameters:
139
- doc: Document to align
140
141
Returns:
142
- Doc: Document with aligned continuation lines
143
"""
144
145
def hang(i, doc):
146
"""
147
Create hanging indent document.
148
149
Parameters:
150
- i (int): Hanging indentation amount
151
- doc: Document to apply hanging indent to
152
153
Returns:
154
- Doc: Document with hanging indentation
155
"""
156
157
def fill(docs):
158
"""
159
Create document that fills lines optimally with content.
160
161
Parameters:
162
- docs: Iterable of documents to fill
163
164
Returns:
165
- Doc: Document with optimal line filling
166
167
Notes:
168
- Attempts to fit as much content as possible on each line
169
- Breaks to new lines when content doesn't fit
170
"""
171
```
172
173
### Document Constants
174
175
Pre-defined document constants for common layout elements.
176
177
```python { .api }
178
# Document constants
179
NIL # Empty document (no output)
180
LINE # Line break or space (context-dependent)
181
SOFTLINE # Line break or nothing (context-dependent)
182
HARDLINE # Forced line break
183
```
184
185
## Usage Examples
186
187
### Basic Document Construction
188
189
```python
190
from prettyprinter.doc import concat, group, nest, always_break
191
from prettyprinter.doc import NIL, LINE, SOFTLINE, HARDLINE
192
193
# Simple concatenation
194
doc = concat(['Hello', ' ', 'World'])
195
196
# Grouped content that tries to fit on one line
197
grouped = group(concat(['[', '1', ',', LINE, '2', ',', LINE, '3', ']']))
198
199
# Nested indentation
200
indented = nest(4, concat(['def foo():', HARDLINE, 'return 42']))
201
```
202
203
### Custom Pretty Printer with Documents
204
205
```python
206
from prettyprinter import register_pretty
207
from prettyprinter.doc import concat, group, nest, annotate
208
from prettyprinter.doc import LINE, HARDLINE
209
from prettyprinter.syntax import Token
210
211
class Matrix:
212
def __init__(self, rows):
213
self.rows = rows
214
215
@register_pretty(Matrix)
216
def pretty_matrix(matrix, ctx):
217
# Create rows as documents
218
row_docs = []
219
for row in matrix.rows:
220
row_doc = concat([
221
'[',
222
concat([
223
str(item) if i == 0 else concat([',', LINE, str(item)])
224
for i, item in enumerate(row)
225
]),
226
']'
227
])
228
row_docs.append(row_doc)
229
230
# Combine rows with proper indentation
231
return group(concat([
232
annotate(Token.NAME_FUNCTION, 'Matrix'),
233
'(',
234
nest(ctx.indent, concat([
235
HARDLINE,
236
concat([
237
row_doc if i == 0 else concat([',', HARDLINE, row_doc])
238
for i, row_doc in enumerate(row_docs)
239
])
240
])),
241
HARDLINE,
242
')'
243
]))
244
245
# Usage
246
matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
247
pprint(matrix)
248
```
249
250
### Contextual Document Example
251
252
```python
253
from prettyprinter.doc import contextual, concat
254
from prettyprinter import register_pretty
255
256
class AdaptiveList:
257
def __init__(self, items):
258
self.items = items
259
260
@register_pretty(AdaptiveList)
261
def pretty_adaptive_list(alist, ctx):
262
def make_adaptive_doc(indent, column, page_width, ribbon_width):
263
available_width = page_width - column
264
265
if available_width > 50:
266
# Wide format: all on one line
267
return concat([
268
'AdaptiveList([',
269
', '.join(str(item) for item in alist.items),
270
'])'
271
])
272
else:
273
# Narrow format: vertical layout
274
item_docs = [concat([str(item), ',' if i < len(alist.items)-1 else ''])
275
for i, item in enumerate(alist.items)]
276
return concat([
277
'AdaptiveList([',
278
nest(4, concat([HARDLINE, *[concat([item_doc, HARDLINE])
279
for item_doc in item_docs]])),
280
'])'
281
])
282
283
return contextual(make_adaptive_doc)
284
```
285
286
### Advanced Layout with Flat Choice
287
288
```python
289
from prettyprinter.doc import flat_choice, concat, always_break
290
from prettyprinter import register_pretty
291
292
class FlexibleDict:
293
def __init__(self, data):
294
self.data = data
295
296
@register_pretty(FlexibleDict)
297
def pretty_flexible_dict(fdict, ctx):
298
if not fdict.data:
299
return 'FlexibleDict({})'
300
301
# Create key-value pair documents
302
pairs = []
303
for key, value in fdict.data.items():
304
pair = flat_choice(
305
when_broken=concat([
306
repr(key), ':', HARDLINE,
307
nest(4, str(value))
308
]),
309
when_flat=concat([repr(key), ': ', str(value)])
310
)
311
pairs.append(pair)
312
313
# Combine pairs
314
combined = concat([
315
pairs[0] if i == 0 else concat([',', LINE, pair])
316
for i, pair in enumerate(pairs)
317
])
318
319
return group(concat([
320
'FlexibleDict({',
321
nest(4, concat([SOFTLINE, combined])),
322
SOFTLINE,
323
'})'
324
]))
325
```
326
327
### Document Validation and Debugging
328
329
```python
330
from prettyprinter.doc import concat
331
from prettyprinter.doctypes import Doc
332
333
def debug_document_structure(doc, level=0):
334
"""Print document structure for debugging."""
335
indent = ' ' * level
336
337
if isinstance(doc, str):
338
print(f"{indent}String: {repr(doc)}")
339
elif isinstance(doc, Doc):
340
print(f"{indent}{type(doc).__name__}")
341
if hasattr(doc, 'docs'): # Concat, Fill
342
for subdoc in doc.docs:
343
debug_document_structure(subdoc, level + 1)
344
elif hasattr(doc, 'doc'): # Group, Nest, AlwaysBreak, Annotated
345
debug_document_structure(doc.doc, level + 1)
346
elif hasattr(doc, 'when_flat'): # FlatChoice
347
print(f"{indent} when_flat:")
348
debug_document_structure(doc.when_flat, level + 2)
349
print(f"{indent} when_broken:")
350
debug_document_structure(doc.when_broken, level + 2)
351
352
# Example usage
353
doc = group(concat(['hello', LINE, 'world']))
354
debug_document_structure(doc)
355
```
356
357
### Custom Document Types
358
359
```python
360
from prettyprinter.doctypes import Doc
361
from prettyprinter.doc import concat
362
363
class Highlight(Doc):
364
"""Custom document type for highlighting."""
365
366
def __init__(self, doc, color='red'):
367
self.doc = doc
368
self.color = color
369
370
def normalize(self):
371
from prettyprinter.doc import validate_doc # Internal function
372
return Highlight(validate_doc(self.doc), self.color)
373
374
def __repr__(self):
375
return f'Highlight({self.doc!r}, {self.color!r})'
376
377
# Example class
378
class ImportantData:
379
def __init__(self, value):
380
self.value = value
381
382
# Use in pretty printer
383
@register_pretty(ImportantData)
384
def pretty_important_data(data, ctx):
385
return concat([
386
'ImportantData(',
387
Highlight(str(data.value), 'yellow'),
388
')'
389
])
390
```