Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/awscli/bcdoc/textwriter.py
1566 views
1
# -*- coding: utf-8 -*-
2
"""
3
4
Custom docutils writer for plain text.
5
Based heavily on the Sphinx text writer. See copyright below.
6
7
:copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
8
:license: BSD, see LICENSE for details.
9
10
"""
11
import os
12
import re
13
import textwrap
14
15
from docutils import nodes, writers
16
17
18
class TextWrapper(textwrap.TextWrapper):
19
"""Custom subclass that uses a different word separator regex."""
20
21
wordsep_re = re.compile(
22
r'(\s+|' # any whitespace
23
r'(?<=\s)(?::[a-z-]+:)?`\S+|' # interpreted text start
24
r'[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|' # hyphenated words
25
r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash
26
27
28
MAXWIDTH = 70
29
STDINDENT = 3
30
31
32
def my_wrap(text, width=MAXWIDTH, **kwargs):
33
w = TextWrapper(width=width, **kwargs)
34
return w.wrap(text)
35
36
37
class TextWriter(writers.Writer):
38
supported = ('text',)
39
settings_spec = ('No options here.', '', ())
40
settings_defaults = {}
41
42
output = None
43
44
def __init__(self):
45
writers.Writer.__init__(self)
46
47
def translate(self):
48
visitor = TextTranslator(self.document)
49
self.document.walkabout(visitor)
50
self.output = visitor.body
51
52
53
class TextTranslator(nodes.NodeVisitor):
54
sectionchars = '*=-~"+`'
55
56
def __init__(self, document):
57
nodes.NodeVisitor.__init__(self, document)
58
59
self.nl = os.linesep
60
self.states = [[]]
61
self.stateindent = [0]
62
self.list_counter = []
63
self.sectionlevel = 0
64
self.table = None
65
66
def add_text(self, text):
67
self.states[-1].append((-1, text))
68
69
def new_state(self, indent=STDINDENT):
70
self.states.append([])
71
self.stateindent.append(indent)
72
73
def end_state(self, wrap=True, end=[''], first=None):
74
content = self.states.pop()
75
maxindent = sum(self.stateindent)
76
indent = self.stateindent.pop()
77
result = []
78
toformat = []
79
80
def do_format():
81
if not toformat:
82
return
83
if wrap:
84
res = my_wrap(''.join(toformat), width=MAXWIDTH-maxindent)
85
else:
86
res = ''.join(toformat).splitlines()
87
if end:
88
res += end
89
result.append((indent, res))
90
for itemindent, item in content:
91
if itemindent == -1:
92
toformat.append(item)
93
else:
94
do_format()
95
result.append((indent + itemindent, item))
96
toformat = []
97
do_format()
98
if first is not None and result:
99
itemindent, item = result[0]
100
if item:
101
result.insert(0, (itemindent - indent, [first + item[0]]))
102
result[1] = (itemindent, item[1:])
103
self.states[-1].extend(result)
104
105
def visit_document(self, node):
106
self.new_state(0)
107
108
def depart_document(self, node):
109
self.end_state()
110
self.body = self.nl.join(line and (' '*indent + line)
111
for indent, lines in self.states[0]
112
for line in lines)
113
# XXX header/footer?
114
115
def visit_highlightlang(self, node):
116
raise nodes.SkipNode
117
118
def visit_section(self, node):
119
self._title_char = self.sectionchars[self.sectionlevel]
120
self.sectionlevel += 1
121
122
def depart_section(self, node):
123
self.sectionlevel -= 1
124
125
def visit_topic(self, node):
126
self.new_state(0)
127
128
def depart_topic(self, node):
129
self.end_state()
130
131
visit_sidebar = visit_topic
132
depart_sidebar = depart_topic
133
134
def visit_rubric(self, node):
135
self.new_state(0)
136
self.add_text('-[ ')
137
138
def depart_rubric(self, node):
139
self.add_text(' ]-')
140
self.end_state()
141
142
def visit_compound(self, node):
143
pass
144
145
def depart_compound(self, node):
146
pass
147
148
def visit_glossary(self, node):
149
pass
150
151
def depart_glossary(self, node):
152
pass
153
154
def visit_title(self, node):
155
if isinstance(node.parent, nodes.Admonition):
156
self.add_text(node.astext()+': ')
157
raise nodes.SkipNode
158
self.new_state(0)
159
160
def depart_title(self, node):
161
if isinstance(node.parent, nodes.section):
162
char = self._title_char
163
else:
164
char = '^'
165
text = ''.join(x[1] for x in self.states.pop() if x[0] == -1)
166
self.stateindent.pop()
167
self.states[-1].append((0, ['', text, '%s' % (char * len(text)), '']))
168
169
def visit_subtitle(self, node):
170
pass
171
172
def depart_subtitle(self, node):
173
pass
174
175
def visit_attribution(self, node):
176
self.add_text('-- ')
177
178
def depart_attribution(self, node):
179
pass
180
181
def visit_desc(self, node):
182
pass
183
184
def depart_desc(self, node):
185
pass
186
187
def visit_desc_signature(self, node):
188
self.new_state(0)
189
if node.parent['objtype'] in ('class', 'exception'):
190
self.add_text('%s ' % node.parent['objtype'])
191
192
def depart_desc_signature(self, node):
193
# XXX: wrap signatures in a way that makes sense
194
self.end_state(wrap=False, end=None)
195
196
def visit_desc_name(self, node):
197
pass
198
199
def depart_desc_name(self, node):
200
pass
201
202
def visit_desc_addname(self, node):
203
pass
204
205
def depart_desc_addname(self, node):
206
pass
207
208
def visit_desc_type(self, node):
209
pass
210
211
def depart_desc_type(self, node):
212
pass
213
214
def visit_desc_returns(self, node):
215
self.add_text(' -> ')
216
217
def depart_desc_returns(self, node):
218
pass
219
220
def visit_desc_parameterlist(self, node):
221
self.add_text('(')
222
self.first_param = 1
223
224
def depart_desc_parameterlist(self, node):
225
self.add_text(')')
226
227
def visit_desc_parameter(self, node):
228
if not self.first_param:
229
self.add_text(', ')
230
else:
231
self.first_param = 0
232
self.add_text(node.astext())
233
raise nodes.SkipNode
234
235
def visit_desc_optional(self, node):
236
self.add_text('[')
237
238
def depart_desc_optional(self, node):
239
self.add_text(']')
240
241
def visit_desc_annotation(self, node):
242
pass
243
244
def depart_desc_annotation(self, node):
245
pass
246
247
def visit_refcount(self, node):
248
pass
249
250
def depart_refcount(self, node):
251
pass
252
253
def visit_desc_content(self, node):
254
self.new_state()
255
self.add_text(self.nl)
256
257
def depart_desc_content(self, node):
258
self.end_state()
259
260
def visit_figure(self, node):
261
self.new_state()
262
263
def depart_figure(self, node):
264
self.end_state()
265
266
def visit_caption(self, node):
267
pass
268
269
def depart_caption(self, node):
270
pass
271
272
def visit_productionlist(self, node):
273
self.new_state()
274
names = []
275
for production in node:
276
names.append(production['tokenname'])
277
maxlen = max(len(name) for name in names)
278
for production in node:
279
if production['tokenname']:
280
self.add_text(production['tokenname'].ljust(maxlen) + ' ::=')
281
lastname = production['tokenname']
282
else:
283
self.add_text('%s ' % (' '*len(lastname)))
284
self.add_text(production.astext() + self.nl)
285
self.end_state(wrap=False)
286
raise nodes.SkipNode
287
288
def visit_seealso(self, node):
289
self.new_state()
290
291
def depart_seealso(self, node):
292
self.end_state(first='')
293
294
def visit_footnote(self, node):
295
self._footnote = node.children[0].astext().strip()
296
self.new_state(len(self._footnote) + 3)
297
298
def depart_footnote(self, node):
299
self.end_state(first='[%s] ' % self._footnote)
300
301
def visit_citation(self, node):
302
if len(node) and isinstance(node[0], nodes.label):
303
self._citlabel = node[0].astext()
304
else:
305
self._citlabel = ''
306
self.new_state(len(self._citlabel) + 3)
307
308
def depart_citation(self, node):
309
self.end_state(first='[%s] ' % self._citlabel)
310
311
def visit_label(self, node):
312
raise nodes.SkipNode
313
314
# XXX: option list could use some better styling
315
316
def visit_option_list(self, node):
317
pass
318
319
def depart_option_list(self, node):
320
pass
321
322
def visit_option_list_item(self, node):
323
self.new_state(0)
324
325
def depart_option_list_item(self, node):
326
self.end_state()
327
328
def visit_option_group(self, node):
329
self._firstoption = True
330
331
def depart_option_group(self, node):
332
self.add_text(' ')
333
334
def visit_option(self, node):
335
if self._firstoption:
336
self._firstoption = False
337
else:
338
self.add_text(', ')
339
340
def depart_option(self, node):
341
pass
342
343
def visit_option_string(self, node):
344
pass
345
346
def depart_option_string(self, node):
347
pass
348
349
def visit_option_argument(self, node):
350
self.add_text(node['delimiter'])
351
352
def depart_option_argument(self, node):
353
pass
354
355
def visit_description(self, node):
356
pass
357
358
def depart_description(self, node):
359
pass
360
361
def visit_tabular_col_spec(self, node):
362
raise nodes.SkipNode
363
364
def visit_colspec(self, node):
365
self.table[0].append(node['colwidth'])
366
raise nodes.SkipNode
367
368
def visit_tgroup(self, node):
369
pass
370
371
def depart_tgroup(self, node):
372
pass
373
374
def visit_thead(self, node):
375
pass
376
377
def depart_thead(self, node):
378
pass
379
380
def visit_tbody(self, node):
381
self.table.append('sep')
382
383
def depart_tbody(self, node):
384
pass
385
386
def visit_row(self, node):
387
self.table.append([])
388
389
def depart_row(self, node):
390
pass
391
392
def visit_entry(self, node):
393
if 'morerows' in node or 'morecols' in node:
394
raise NotImplementedError('Column or row spanning cells are '
395
'not implemented.')
396
self.new_state(0)
397
398
def depart_entry(self, node):
399
text = self.nl.join(self.nl.join(x[1]) for x in self.states.pop())
400
self.stateindent.pop()
401
self.table[-1].append(text)
402
403
def visit_table(self, node):
404
if self.table:
405
raise NotImplementedError('Nested tables are not supported.')
406
self.new_state(0)
407
self.table = [[]]
408
409
def depart_table(self, node):
410
lines = self.table[1:]
411
fmted_rows = []
412
colwidths = self.table[0]
413
realwidths = colwidths[:]
414
separator = 0
415
# don't allow paragraphs in table cells for now
416
for line in lines:
417
if line == 'sep':
418
separator = len(fmted_rows)
419
else:
420
cells = []
421
for i, cell in enumerate(line):
422
par = my_wrap(cell, width=colwidths[i])
423
if par:
424
maxwidth = max(map(len, par))
425
else:
426
maxwidth = 0
427
realwidths[i] = max(realwidths[i], maxwidth)
428
cells.append(par)
429
fmted_rows.append(cells)
430
431
def writesep(char='-'):
432
out = ['+']
433
for width in realwidths:
434
out.append(char * (width+2))
435
out.append('+')
436
self.add_text(''.join(out) + self.nl)
437
438
def writerow(row):
439
lines = zip(*row)
440
for line in lines:
441
out = ['|']
442
for i, cell in enumerate(line):
443
if cell:
444
out.append(' ' + cell.ljust(realwidths[i]+1))
445
else:
446
out.append(' ' * (realwidths[i] + 2))
447
out.append('|')
448
self.add_text(''.join(out) + self.nl)
449
450
for i, row in enumerate(fmted_rows):
451
if separator and i == separator:
452
writesep('=')
453
else:
454
writesep('-')
455
writerow(row)
456
writesep('-')
457
self.table = None
458
self.end_state(wrap=False)
459
460
def visit_acks(self, node):
461
self.new_state(0)
462
self.add_text(
463
', '.join(n.astext() for n in node.children[0].children) + '.')
464
self.end_state()
465
raise nodes.SkipNode
466
467
def visit_image(self, node):
468
if 'alt' in node.attributes:
469
self.add_text(_('[image: %s]') % node['alt'])
470
self.add_text(_('[image]'))
471
raise nodes.SkipNode
472
473
def visit_transition(self, node):
474
indent = sum(self.stateindent)
475
self.new_state(0)
476
self.add_text('=' * (MAXWIDTH - indent))
477
self.end_state()
478
raise nodes.SkipNode
479
480
def visit_bullet_list(self, node):
481
self.list_counter.append(-1)
482
483
def depart_bullet_list(self, node):
484
self.list_counter.pop()
485
486
def visit_enumerated_list(self, node):
487
self.list_counter.append(0)
488
489
def depart_enumerated_list(self, node):
490
self.list_counter.pop()
491
492
def visit_definition_list(self, node):
493
self.list_counter.append(-2)
494
495
def depart_definition_list(self, node):
496
self.list_counter.pop()
497
498
def visit_list_item(self, node):
499
if self.list_counter[-1] == -1:
500
# bullet list
501
self.new_state(2)
502
elif self.list_counter[-1] == -2:
503
# definition list
504
pass
505
else:
506
# enumerated list
507
self.list_counter[-1] += 1
508
self.new_state(len(str(self.list_counter[-1])) + 2)
509
510
def depart_list_item(self, node):
511
if self.list_counter[-1] == -1:
512
self.end_state(first='* ', end=None)
513
elif self.list_counter[-1] == -2:
514
pass
515
else:
516
self.end_state(first='%s. ' % self.list_counter[-1], end=None)
517
518
def visit_definition_list_item(self, node):
519
self._li_has_classifier = len(node) >= 2 and \
520
isinstance(node[1], nodes.classifier)
521
522
def depart_definition_list_item(self, node):
523
pass
524
525
def visit_term(self, node):
526
self.new_state(0)
527
528
def depart_term(self, node):
529
if not self._li_has_classifier:
530
self.end_state(end=None)
531
532
def visit_termsep(self, node):
533
self.add_text(', ')
534
raise nodes.SkipNode
535
536
def visit_classifier(self, node):
537
self.add_text(' : ')
538
539
def depart_classifier(self, node):
540
self.end_state(end=None)
541
542
def visit_definition(self, node):
543
self.new_state()
544
545
def depart_definition(self, node):
546
self.end_state()
547
548
def visit_field_list(self, node):
549
pass
550
551
def depart_field_list(self, node):
552
pass
553
554
def visit_field(self, node):
555
pass
556
557
def depart_field(self, node):
558
pass
559
560
def visit_field_name(self, node):
561
self.new_state(0)
562
563
def depart_field_name(self, node):
564
self.add_text(':')
565
self.end_state(end=None)
566
567
def visit_field_body(self, node):
568
self.new_state()
569
570
def depart_field_body(self, node):
571
self.end_state()
572
573
def visit_centered(self, node):
574
pass
575
576
def depart_centered(self, node):
577
pass
578
579
def visit_hlist(self, node):
580
pass
581
582
def depart_hlist(self, node):
583
pass
584
585
def visit_hlistcol(self, node):
586
pass
587
588
def depart_hlistcol(self, node):
589
pass
590
591
def visit_admonition(self, node):
592
self.new_state(0)
593
594
def depart_admonition(self, node):
595
self.end_state()
596
597
def visit_versionmodified(self, node):
598
self.new_state(0)
599
600
def depart_versionmodified(self, node):
601
self.end_state()
602
603
def visit_literal_block(self, node):
604
self.new_state()
605
606
def depart_literal_block(self, node):
607
self.end_state(wrap=False)
608
609
def visit_doctest_block(self, node):
610
self.new_state(0)
611
612
def depart_doctest_block(self, node):
613
self.end_state(wrap=False)
614
615
def visit_line_block(self, node):
616
self.new_state(0)
617
618
def depart_line_block(self, node):
619
self.end_state(wrap=False)
620
621
def visit_line(self, node):
622
pass
623
624
def depart_line(self, node):
625
pass
626
627
def visit_block_quote(self, node):
628
self.new_state()
629
630
def depart_block_quote(self, node):
631
self.end_state()
632
633
def visit_compact_paragraph(self, node):
634
pass
635
636
def depart_compact_paragraph(self, node):
637
pass
638
639
def visit_paragraph(self, node):
640
self.new_state(0)
641
642
def depart_paragraph(self, node):
643
self.end_state()
644
645
def visit_target(self, node):
646
raise nodes.SkipNode
647
648
def visit_index(self, node):
649
raise nodes.SkipNode
650
651
def visit_substitution_definition(self, node):
652
raise nodes.SkipNode
653
654
def visit_pending_xref(self, node):
655
pass
656
657
def depart_pending_xref(self, node):
658
pass
659
660
def visit_reference(self, node):
661
pass
662
663
def depart_reference(self, node):
664
pass
665
666
def visit_download_reference(self, node):
667
pass
668
669
def depart_download_reference(self, node):
670
pass
671
672
def visit_emphasis(self, node):
673
self.add_text('*')
674
675
def depart_emphasis(self, node):
676
self.add_text('*')
677
678
def visit_literal_emphasis(self, node):
679
self.add_text('*')
680
681
def depart_literal_emphasis(self, node):
682
self.add_text('*')
683
684
def visit_strong(self, node):
685
self.add_text('**')
686
687
def depart_strong(self, node):
688
self.add_text('**')
689
690
def visit_abbreviation(self, node):
691
self.add_text('')
692
693
def depart_abbreviation(self, node):
694
if node.hasattr('explanation'):
695
self.add_text(' (%s)' % node['explanation'])
696
697
def visit_title_reference(self, node):
698
self.add_text('*')
699
700
def depart_title_reference(self, node):
701
self.add_text('*')
702
703
def visit_literal(self, node):
704
self.add_text('"')
705
706
def depart_literal(self, node):
707
self.add_text('"')
708
709
def visit_subscript(self, node):
710
self.add_text('_')
711
712
def depart_subscript(self, node):
713
pass
714
715
def visit_superscript(self, node):
716
self.add_text('^')
717
718
def depart_superscript(self, node):
719
pass
720
721
def visit_footnote_reference(self, node):
722
self.add_text('[%s]' % node.astext())
723
raise nodes.SkipNode
724
725
def visit_citation_reference(self, node):
726
self.add_text('[%s]' % node.astext())
727
raise nodes.SkipNode
728
729
def visit_Text(self, node):
730
self.add_text(node.astext())
731
732
def depart_Text(self, node):
733
pass
734
735
def visit_generated(self, node):
736
pass
737
738
def depart_generated(self, node):
739
pass
740
741
def visit_inline(self, node):
742
pass
743
744
def depart_inline(self, node):
745
pass
746
747
def visit_problematic(self, node):
748
self.add_text('>>')
749
750
def depart_problematic(self, node):
751
self.add_text('<<')
752
753
def visit_system_message(self, node):
754
self.new_state(0)
755
self.add_text('<SYSTEM MESSAGE: %s>' % node.astext())
756
self.end_state()
757
raise nodes.SkipNode
758
759
def visit_comment(self, node):
760
raise nodes.SkipNode
761
762
def visit_meta(self, node):
763
# only valid for HTML
764
raise nodes.SkipNode
765
766
def visit_raw(self, node):
767
if 'text' in node.get('format', '').split():
768
self.body.append(node.astext())
769
raise nodes.SkipNode
770
771
def _visit_admonition(self, node):
772
self.new_state(2)
773
774
def _make_depart_admonition(name):
775
def depart_admonition(self, node):
776
self.end_state(first=name.capitalize() + ': ')
777
return depart_admonition
778
779
visit_attention = _visit_admonition
780
depart_attention = _make_depart_admonition('attention')
781
visit_caution = _visit_admonition
782
depart_caution = _make_depart_admonition('caution')
783
visit_danger = _visit_admonition
784
depart_danger = _make_depart_admonition('danger')
785
visit_error = _visit_admonition
786
depart_error = _make_depart_admonition('error')
787
visit_hint = _visit_admonition
788
depart_hint = _make_depart_admonition('hint')
789
visit_important = _visit_admonition
790
depart_important = _make_depart_admonition('important')
791
visit_note = _visit_admonition
792
depart_note = _make_depart_admonition('note')
793
visit_tip = _visit_admonition
794
depart_tip = _make_depart_admonition('tip')
795
visit_warning = _visit_admonition
796
depart_warning = _make_depart_admonition('warning')
797
798
def unknown_visit(self, node):
799
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
800
801