Passed
Push — develop ( 7bf5fe...0379af )
by Christophe
01:00
created

pandoc_numbering.finalize()   F

Complexity

Conditions 13

Size

Total Lines 123
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 69
nop 1
dl 0
loc 123
rs 3.9545
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like pandoc_numbering.finalize() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python
2
3
# pylint: disable=too-many-lines
4
5
"""Pandoc filter to number all kinds of things."""
6
7
import copy
8
import re
9
import unicodedata
10
from functools import partial
11
from textwrap import dedent
12
13
from panflute import (
14
    BlockQuote,
15
    BulletList,
16
    Citation,
17
    Cite,
18
    CodeBlock,
19
    Definition,
20
    DefinitionItem,
21
    DefinitionList,
22
    Div,
23
    Emph,
24
    Header,
25
    HorizontalRule,
26
    Image,
27
    LineBlock,
28
    LineBreak,
29
    LineItem,
30
    Link,
31
    ListItem,
32
    MetaBool,
33
    MetaInlines,
34
    MetaList,
35
    MetaMap,
36
    MetaString,
37
    Note,
38
    Para,
39
    Plain,
40
    RawBlock,
41
    RawInline,
42
    SoftBreak,
43
    Space,
44
    Span,
45
    Str,
46
    Strong,
47
    Table,
48
    TableCell,
49
    TableRow,
50
    convert_text,
51
    debug,
52
    run_filters,
53
    stringify,
54
)
55
56
57
# pylint: disable=bad-option-value,useless-object-inheritance
58
class Numbered:
59
    """
60
    Numbered elements.
61
    """
62
63
    # pylint: disable=too-many-instance-attributes
64
    __slots__ = [
65
        "_elem",
66
        "_doc",
67
        "_match",
68
        "_tag",
69
        "_entry",
70
        "_link",
71
        "_caption",
72
        "_title",
73
        "_description",
74
        "_category",
75
        "_basic_category",
76
        "_first_section_level",
77
        "_last_section_level",
78
        "_leading",
79
        "_number",
80
        "_global_number",
81
        "_section_number",
82
        "_local_number",
83
        "_section_alias",
84
        "_alias",
85
    ]
86
87
    @property
88
    def tag(self):
89
        """
90
        Get the tag property.
91
92
        Returns
93
        -------
94
            The tag property.
95
        """
96
        return self._tag
97
98
    @property
99
    def entry(self):
100
        """
101
        Get the entry property.
102
103
        Returns
104
        -------
105
            The entry property.
106
        """
107
        return self._entry
108
109
    @property
110
    def link(self):
111
        """
112
        Get the link property.
113
114
        Returns
115
        -------
116
            The link property.
117
        """
118
        return self._link
119
120
    @property
121
    def title(self):
122
        """
123
        Get the title property.
124
125
        Returns
126
        -------
127
            The title property.
128
        """
129
        return self._title
130
131
    @property
132
    def description(self):
133
        """
134
        Get the description property.
135
136
        Returns
137
        -------
138
            The description property.
139
        """
140
        return self._description
141
142
    @property
143
    def global_number(self):
144
        """
145
        Get the global_number property.
146
147
        Returns
148
        -------
149
            The global_number property.
150
        """
151
        return self._global_number
152
153
    @property
154
    def section_number(self):
155
        """
156
        Get the section_number property.
157
158
        Returns
159
        -------
160
            The section_number property.
161
        """
162
        return self._section_number
163
164
    @property
165
    def section_alias(self):
166
        """
167
        Get the section_alias property.
168
169
        Returns
170
        -------
171
            The section_alias property.
172
        """
173
        return self._section_alias
174
175
    @property
176
    def alias(self):
177
        """
178
        Get the alias property.
179
180
        Returns
181
        -------
182
            The alias property.
183
        """
184
        return self._alias
185
186
    @property
187
    def local_number(self):
188
        """
189
        Get the local_number property.
190
191
        Returns
192
        -------
193
            The local_number property.
194
        """
195
        return self._local_number
196
197
    @property
198
    def category(self):
199
        """
200
        Get the category property.
201
202
        Returns
203
        -------
204
            The category property.
205
        """
206
        return self._category
207
208
    @property
209
    def caption(self):
210
        """
211
        Get the caption property.
212
213
        Returns
214
        -------
215
            The caption property.
216
        """
217
        return self._caption
218
219
    number_regex = "#((?P<prefix>[a-zA-Z][\\w.-]*):)?(?P<name>[a-zA-Z][\\w:.-]*)?"
220
    _regex = "(?P<header>(?P<hidden>(-\\.)*)(\\+\\.)*)"
221
    header_regex = "^" + _regex + "$"
222
    marker_regex = "^" + _regex + number_regex + "$"
223
    double_sharp_regex = "^" + _regex + "#" + number_regex + "$"
224
225
    @staticmethod
226
    def _remove_accents(string):
227
        nfkd_form = unicodedata.normalize("NFKD", string)
228
        # pylint: disable=redundant-u-string-prefix
229
        return "".join([c for c in nfkd_form if not unicodedata.combining(c)])
230
231
    @staticmethod
232
    def _identifier(string):
233
        # replace invalid characters by dash
234
        string = re.sub(
235
            "[^0-9a-zA-Z_-]+", "-", Numbered._remove_accents(string.lower())
236
        )
237
238
        # Remove leading digits
239
        return re.sub("^[^a-zA-Z]+", "", string)
240
241
    def __init__(self, elem, doc):
242
        """
243
        Initialise an instance.
244
245
        Arguments
246
        ---------
247
        elem
248
            An element.
249
        doc
250
            The document.
251
        """
252
        self._elem = elem
253
        self._doc = doc
254
        self._tag = None
255
        self._entry = Span(classes=["pandoc-numbering-entry"])
256
        self._link = Span(classes=["pandoc-numbering-link"])
257
        self._caption = None
258
        self._title = None
259
        self._description = None
260
        self._category = None
261
        self._basic_category = None
262
        self._first_section_level = None
263
        self._last_section_level = None
264
        self._leading = None
265
        self._number = None
266
        self._global_number = None
267
        self._section_number = None
268
        self._local_number = None
269
        self._section_alias = None
270
        self._alias = None
271
272
        if self._get_content() and isinstance(self._get_content()[-1], Str):
273
            self._match = re.match(Numbered.marker_regex, self._get_content()[-1].text)
274
            if self._match:
275
                self._replace_marker()
276
            elif re.match(Numbered.double_sharp_regex, self._get_content()[-1].text):
277
                self._replace_double_sharp()
278
279
    def _set_content(self, content):
280
        if isinstance(self._elem, Para):
281
            self._elem.content = content
282
        elif isinstance(self._elem, DefinitionItem):
283
            self._elem.term = content
284
285
    def _get_content(self):
286
        if isinstance(self._elem, Para):
287
            return self._elem.content
288
        if isinstance(self._elem, DefinitionItem):
289
            return self._elem.term
290
        return None
291
292
    def _replace_double_sharp(self):
293
        self._get_content()[-1].text = self._get_content()[-1].text.replace(
294
            "##", "#", 1
295
        )
296
297
    def _replace_marker(self):
298
        self._compute_title()
299
        self._compute_description()
300
        self._compute_basic_category()
301
        self._compute_levels()
302
        self._compute_section_number()
303
        self._compute_section_alias()
304
        self._compute_leading()
305
        self._compute_category()
306
        self._compute_number()
307
        self._compute_tag()
308
        self._compute_alias()
309
        self._compute_local_number()
310
        self._compute_global_number()
311
        self._compute_data()
312
313
    def _compute_title(self):
314
        self._title = []
315
        if (
316
            isinstance(self._get_content()[-3], Str)
317
            and self._get_content()[-3].text[-1:] == ")"
318
        ):
319
            for i, item in enumerate(self._get_content()):
320
                if isinstance(item, Str) and item.text[0] == "(":
321
                    self._title = self._get_content()[i:-2]
322
                    # Detach from original parent
323
                    self._title.parent = None
324
                    self._title[0].text = self._title[0].text[1:]
325
                    self._title[-1].text = self._title[-1].text[:-1]
326
                    del self._get_content()[i - 1 : -2]
327
                    break
328
        self._title = list(self._title)
329
330
    def _compute_description(self):
331
        self._description = self._get_content()[:-2]
332
        # Detach from original parent
333
        self._description.parent = None
334
        self._description = list(self._description)
335
336
    def _compute_basic_category(self):
337
        if self._match.group("prefix") is None:
338
            self._basic_category = Numbered._identifier(
339
                "".join(map(stringify, self._description))
340
            )
341
        else:
342
            self._basic_category = self._match.group("prefix")
343
        if self._basic_category not in self._doc.defined:
344
            define(self._basic_category, self._doc)
345
346
    def _compute_levels(self):
347
        # Compute the first and last section level values
348
        self._first_section_level = len(self._match.group("hidden")) // 2
349
        self._last_section_level = len(self._match.group("header")) // 2
350
351
        # Get the default first and last section level
352
        if self._first_section_level == self._last_section_level == 0:
353
            self._first_section_level = self._doc.defined[self._basic_category][
354
                "first-section-level"
355
            ]
356
            self._last_section_level = self._doc.defined[self._basic_category][
357
                "last-section-level"
358
            ]
359
360
    def _compute_section_number(self):
361
        self._section_number = ".".join(
362
            map(str, self._doc.headers[: self._last_section_level])
363
        )
364
365
    def _compute_section_alias(self):
366
        strings = list(map(str, self._doc.aliases[: self._last_section_level]))
367
        for index, string in enumerate(strings):
368
            if string == "":
369
                strings[index] = "0"
370
        self._section_alias = ".".join(strings)
371
372
    def _compute_leading(self):
373
        # Compute the leading (composed of the section numbering and a dot)
374
        if self._last_section_level != 0:
375
            self._leading = self._section_number + "."
376
        else:
377
            self._leading = ""
378
379
    def _compute_category(self):
380
        self._category = self._basic_category + ":" + self._leading
381
382
        # Is it a new category?
383
        if self._category not in self._doc.count:
384
            self._doc.count[self._category] = 0
385
386
        self._doc.count[self._category] = self._doc.count[self._category] + 1
387
388
    def _compute_number(self):
389
        self._number = str(self._doc.count[self._category])
390
391
    def _compute_tag(self):
392
        # Determine the final tag
393
        if self._match.group("name") is None:
394
            self._tag = self._category + self._number
395
        else:
396
            self._tag = self._basic_category + ":" + self._match.group("name")
397
398
        # Compute collections
399
        if self._basic_category not in self._doc.collections:
400
            self._doc.collections[self._basic_category] = []
401
402
        self._doc.collections[self._basic_category].append(self._tag)
403
404
    def _compute_alias(self):
405
        # Determine the final alias
406
        if not self._title:
407
            if self._section_alias:
408
                self._alias = (
409
                    self._basic_category
410
                    + ":"
411
                    + self._section_alias
412
                    + "."
413
                    + self._number
414
                )
415
            else:
416
                self._alias = self._basic_category + ":" + self._number
417
        else:
418
            if self._section_alias:
419
                self._alias = (
420
                    self._basic_category
421
                    + ":"
422
                    + self._section_alias
423
                    + "."
424
                    + Numbered._identifier(stringify(Span(*self._title)))
425
                )
426
            else:
427
                self._alias = (
428
                    self._basic_category
429
                    + ":"
430
                    + Numbered._identifier(stringify(Span(*self._title)))
431
                )
432
433
    def _compute_local_number(self):
434
        # Replace the '-.-.+.+...#' by the category count (omitting the hidden part)
435
        self._local_number = ".".join(
436
            map(
437
                str,
438
                self._doc.headers[self._first_section_level : self._last_section_level]
439
                + [self._number],
440
            )
441
        )
442
443
    def _compute_global_number(self):
444
        # Compute the global number
445
        if self._section_number:
446
            self._global_number = self._section_number + "." + self._number
447
        else:
448
            self._global_number = self._number
449
450
    def _compute_data(self):
451
        # pylint: disable=too-many-statements,no-member
452
        classes = self._doc.defined[self._basic_category]["classes"]
453
        if self._alias == self._tag:
454
            self._set_content(
455
                [
456
                    Span(),
457
                    Span(
458
                        identifier=self._tag,
459
                        classes=["pandoc-numbering-text"] + classes,
460
                    ),
461
                ]
462
            )
463
        else:
464
            self._set_content(
465
                [
466
                    Span(identifier=self._alias),
467
                    Span(
468
                        identifier=self._tag,
469
                        classes=["pandoc-numbering-text"] + classes,
470
                    ),
471
                ]
472
            )
473
        self._link.classes = self._link.classes + classes
474
        self._entry.classes = self._entry.classes + classes
475
476
        # Prepare the final data
477
        if self._title:
478
            self._get_content()[1].content = copy.deepcopy(
479
                self._doc.defined[self._basic_category]["format-text-title"]
480
            )
481
            self._link.content = copy.deepcopy(
482
                self._doc.defined[self._basic_category]["format-link-title"]
483
            )
484
            self._entry.content = copy.deepcopy(
485
                self._doc.defined[self._basic_category]["format-entry-title"]
486
            )
487
            self._caption = self._doc.defined[self._basic_category][
488
                "format-caption-title"
489
            ]
490
        else:
491
            self._get_content()[1].content = copy.deepcopy(
492
                self._doc.defined[self._basic_category]["format-text-classic"]
493
            )
494
            self._link.content = copy.deepcopy(
495
                self._doc.defined[self._basic_category]["format-link-classic"]
496
            )
497
            self._entry.content = copy.deepcopy(
498
                self._doc.defined[self._basic_category]["format-entry-classic"]
499
            )
500
            self._caption = self._doc.defined[self._basic_category][
501
                "format-caption-classic"
502
            ]
503
504
        # Compute caption (delay replacing %c at the end)
505
        title = stringify(Span(*self._title))
506
        description = stringify(Span(*self._description))
507
        self._caption = self._caption.replace("%t", title.lower())
508
        self._caption = self._caption.replace("%T", title)
509
        self._caption = self._caption.replace("%d", description.lower())
510
        self._caption = self._caption.replace("%D", description)
511
        self._caption = self._caption.replace("%s", self._section_number)
512
        self._caption = self._caption.replace("%g", self._global_number)
513
        self._caption = self._caption.replace("%n", self._local_number)
514
        self._caption = self._caption.replace("#", self._local_number)
515
        if self._doc.format in {"tex", "latex"}:
516
            self._caption = self._caption.replace("%p", "\\pageref{" + self._tag + "}")
517
518
        # Compute content
519
        if isinstance(self._elem, DefinitionItem):
520
            replace_description(Plain(*self._elem.term), self._description)
521
            replace_title(Plain(*self._elem.term), self._title)
522
            replace_global_number(Plain(*self._elem.term), self._global_number)
523
            replace_section_number(Plain(*self._elem.term), self._section_number)
524
            replace_local_number(Plain(*self._elem.term), self._local_number)
525
        else:
526
            replace_description(self._elem, self._description)
527
            replace_title(self._elem, self._title)
528
            replace_global_number(self._elem, self._global_number)
529
            replace_section_number(self._elem, self._section_number)
530
            replace_local_number(self._elem, self._local_number)
531
532
        # Compute link
533
        replace_description(self._link, self._description)
534
        replace_title(self._link, self._title)
535
        replace_global_number(self._link, self._global_number)
536
        replace_section_number(self._link, self._section_number)
537
        replace_local_number(self._link, self._local_number)
538
        if self._doc.format in {"tex", "latex"}:
539
            replace_page_number(self._link, self._tag)
540
541
        # Compute entry
542
        replace_description(self._entry, self._description)
543
        replace_title(self._entry, self._title)
544
        replace_global_number(self._entry, self._global_number)
545
        replace_section_number(self._entry, self._section_number)
546
        replace_local_number(self._entry, self._local_number)
547
548
        # Finalize the content
549
        if self._doc.format in {"tex", "latex"}:
550
            latex_category = re.sub("[^a-z]+", "", self._basic_category)
551
            latex = (
552
                "\\phantomsection"
553
                f"\\addcontentsline{{{latex_category}}}{{{latex_category}}}"
554
                f"{{\\protect\\numberline {{{self._leading + self._number}}}"
555
                f"{{\\ignorespaces {to_latex(self._entry)}"
556
                "}}"
557
            )
558
            self._get_content().insert(0, RawInline(latex, "tex"))
559
560
561
def replace_description(where, description):
562
    """
563
    Replace description in where.
564
565
    Arguments
566
    ---------
567
    where
568
        where to replace
569
    description
570
        replace %D and %d by description
571
    """
572
    where.walk(partial(replacing, search="%D", replace=copy.deepcopy(description)))
573
    where.walk(
574
        partial(
575
            replacing,
576
            search="%d",
577
            replace=[item.walk(lowering) for item in copy.deepcopy(description)],
578
        )
579
    )
580
581
582
def replace_title(where, title):
583
    """
584
    Replace title in where.
585
586
    Arguments
587
    ---------
588
    where
589
        where to replace
590
    title
591
        replace %T and %t by title
592
    """
593
    where.walk(partial(replacing, search="%T", replace=copy.deepcopy(title)))
594
    where.walk(
595
        partial(
596
            replacing,
597
            search="%t",
598
            replace=[item.walk(lowering) for item in copy.deepcopy(title)],
599
        )
600
    )
601
602
603
def replace_section_number(where, section_number):
604
    """
605
    Replace section number in where.
606
607
    Arguments
608
    ---------
609
    where
610
        where to replace
611
    section_number
612
        replace %s by section_number
613
    """
614
    where.walk(partial(replacing, search="%s", replace=[Str(section_number)]))
615
616
617
def replace_global_number(where, global_number):
618
    """
619
    Replace global number in where.
620
621
    Arguments
622
    ---------
623
    where
624
        where to replace
625
    global_number
626
        replace %g by global_number
627
    """
628
    where.walk(partial(replacing, search="%g", replace=[Str(global_number)]))
629
630
631
def replace_local_number(where, local_number):
632
    """
633
    Replace local number in where.
634
635
    Arguments
636
    ---------
637
    where
638
        where to replace
639
    local_number
640
        replace %n and # by local_number
641
    """
642
    where.walk(partial(replacing, search="%n", replace=[Str(local_number)]))
643
    where.walk(partial(replacing, search="#", replace=[Str(local_number)]))
644
645
646
def replace_page_number(where, tag):
647
    """
648
    Replace page number in where.
649
650
    Arguments
651
    ---------
652
    where
653
        where to replace
654
    tag
655
        replace %p by tag
656
    """
657
    where.walk(
658
        partial(
659
            replacing, search="%p", replace=[RawInline("\\pageref{" + tag + "}", "tex")]
660
        )
661
    )
662
663
664
def replace_count(where, count):
665
    """
666
    Replace count in where.
667
668
    Arguments
669
    ---------
670
    where
671
        where to replace
672
    count
673
        replace %c by count
674
    """
675
    where.walk(partial(replacing, search="%c", replace=[Str(count)]))
676
677
678
def remove_useless_latex(elem, _):
679
    """
680
    Clean up LaTeX element for entries.
681
682
    Arguments
683
    ---------
684
    elem
685
        elem to scan
686
687
    Returns
688
    -------
689
        []: if elem is an instance to remove
690
        None: otherwise
691
    """
692
    if isinstance(
693
        elem,
694
        (
695
            BlockQuote,
696
            BulletList,
697
            Citation,
698
            Cite,
699
            CodeBlock,
700
            Definition,
701
            DefinitionItem,
702
            DefinitionList,
703
            Div,
704
            Header,
705
            HorizontalRule,
706
            Image,
707
            LineBlock,
708
            LineBreak,
709
            LineItem,
710
            ListItem,
711
            Note,
712
            Para,
713
            RawBlock,
714
            RawInline,
715
            SoftBreak,
716
            Table,
717
            TableCell,
718
            TableRow,
719
        ),
720
    ):
721
        return []
722
    return None
723
724
725
def to_latex(elem):
726
    """
727
    Convert element to LaTeX.
728
729
    Arguments
730
    ---------
731
    elem
732
        elem to convert
733
734
    Returns
735
    -------
736
        LaTex string
737
    """
738
    return convert_text(
739
        run_filters([remove_useless_latex], doc=Plain(elem)),
740
        input_format="panflute",
741
        output_format="latex",
742
        extra_args=["--no-highlight"],
743
    )
744
745
746
def define(category, doc):
747
    """
748
    Define a category in document.
749
750
    Arguments
751
    ---------
752
    category
753
        category to define
754
    doc
755
        pandoc document
756
    """
757
    # pylint: disable=line-too-long
758
    doc.defined[category] = {
759
        "first-section-level": 0,
760
        "last-section-level": 0,
761
        "format-text-classic": [Strong(Str("%D"), Space(), Str("%n"))],
762
        "format-text-title": [
763
            Strong(Str("%D"), Space(), Str("%n")),
764
            Space(),
765
            Emph(Str("(%T)")),
766
        ],
767
        "format-link-classic": [Str("%D"), Space(), Str("%n")],
768
        "format-link-title": [Str("%D"), Space(), Str("%n"), Space(), Str("(%T)")],
769
        "format-caption-classic": "%D %n",
770
        "format-caption-title": "%D %n (%T)",
771
        "format-entry-title": [Str("%T")],
772
        "classes": [category],
773
        "cite-shortcut": True,
774
        "listing-title": None,
775
        "listing-unnumbered": True,
776
        "listing-unlisted": True,
777
        "listing-identifier": True,
778
        "entry-tab": 1.5,
779
        "entry-space": 2.3,
780
    }
781
    if doc.format == "latex":
782
        doc.defined[category]["format-entry-classic"] = [Str("%D")]
783
        doc.defined[category]["entry-tab"] = 1.5
784
        doc.defined[category]["entry-space"] = 2.3
785
    else:
786
        doc.defined[category]["format-entry-classic"] = [Str("%D"), Space(), Str("%g")]
787
788
789
def lowering(elem, _):
790
    """
791
    Lower element.
792
793
    Arguments
794
    ---------
795
    elem
796
        element to lower
797
    """
798
    if isinstance(elem, Str):
799
        elem.text = elem.text.lower()
800
801
802
def replacing(elem, _, search=None, replace=None):
803
    """
804
    Replace an element.
805
806
    Arguments
807
    ---------
808
    elem
809
        element to scan
810
    search
811
        string to search
812
    replace
813
        string to replace
814
815
    Returns
816
    -------
817
        The modified elements.
818
    """
819
    if isinstance(elem, Str):
820
        search_splitted = elem.text.split(search)
821
        if len(search_splitted) > 1:
822
            text = []
823
824
            if search_splitted[0] != "":
825
                text.append(Str(search_splitted[0]))
826
827
            for string in search_splitted[1:]:
828
                text.extend(replace)
829
                if string != "":
830
                    text.append(Str(string))
831
832
            return text
833
834
    return [elem]
835
836
837
def numbering(elem, doc):
838
    """
839
    Add the numbering of an element.
840
841
    Arguments
842
    ---------
843
    elem
844
        element to number
845
    doc
846
        pandoc document
847
    """
848
    if isinstance(elem, Header):
849
        update_header_numbers(elem, doc)
850
        update_header_aliases(elem, doc)
851
    elif isinstance(elem, (Para, DefinitionItem)):
852
        numbered = Numbered(elem, doc)
853
        if numbered.tag is not None:
854
            doc.information[numbered.tag] = numbered
855
856
857
def referencing(elem, doc):
858
    """
859
    Add a reference for an element.
860
861
    Arguments
862
    ---------
863
    elem
864
        element to reference
865
    doc
866
        pandoc document
867
868
    Returns
869
    -------
870
        A Link or None
871
    """
872
    if isinstance(elem, Link):
873
        return referencing_link(elem, doc)
874
    if isinstance(elem, Cite):
875
        return referencing_cite(elem, doc)
876
    if isinstance(elem, Span) and elem.identifier in doc.information:
877
        replace_count(elem, str(doc.count[doc.information[elem.identifier].category]))
878
    return None
879
880
881
def referencing_link(elem, doc):
882
    """
883
    Add a eference link.
884
885
    Arguments
886
    ---------
887
    elem
888
        element to reference
889
    doc
890
        pandoc document
891
    """
892
    match = re.match("^#(?P<tag>([a-zA-Z][\\w:.-]*))$", elem.url)
893
    if match:
894
        tag = match.group("tag")
895
        if tag in doc.information:
896
            replace_title(elem, doc.information[tag].title)
897
            replace_description(elem, doc.information[tag].description)
898
            replace_global_number(elem, doc.information[tag].global_number)
899
            replace_section_number(elem, doc.information[tag].section_number)
900
            replace_local_number(elem, doc.information[tag].local_number)
901
            replace_count(elem, str(doc.count[doc.information[tag].category]))
902
            if doc.format in {"tex", "latex"}:
903
                replace_page_number(elem, tag)
904
905
            title = stringify(Span(*doc.information[tag].title))
906
            description = stringify(Span(*doc.information[tag].description))
907
            elem.title = elem.title.replace("%t", title.lower())
908
            elem.title = elem.title.replace("%T", title)
909
            elem.title = elem.title.replace("%d", description.lower())
910
            elem.title = elem.title.replace("%D", description)
911
            elem.title = elem.title.replace("%s", doc.information[tag].section_number)
912
            elem.title = elem.title.replace("%g", doc.information[tag].global_number)
913
            elem.title = elem.title.replace("%n", doc.information[tag].local_number)
914
            elem.title = elem.title.replace("#", doc.information[tag].local_number)
915
            elem.title = elem.title.replace(
916
                "%c", str(doc.count[doc.information[tag].category])
917
            )
918
            if doc.format in {"tex", "latex"}:
919
                elem.title = elem.title.replace("%p", "\\pageref{" + tag + "}")
920
921
922
def referencing_cite(elem, doc):
923
    """
924
    Cite reference.
925
926
    Arguments
927
    ---------
928
    elem
929
        element to reference
930
    doc
931
        pandoc document
932
933
    Returns
934
    -------
935
        A Link or None
936
    """
937
    if len(elem.content) == 1 and isinstance(elem.content[0], Str):
938
        match = re.match(
939
            "^(@(?P<tag>(?P<category>[a-zA-Z][\\w.-]*):"
940
            "(([a-zA-Z][\\w.-]*)|(\\d*(\\.\\d*)*))))$",
941
            elem.content[0].text,
942
        )
943
        if match:
944
            category = match.group("category")
945
            if category in doc.defined and doc.defined[category]["cite-shortcut"]:
946
                # Deal with @prefix:name shortcut
947
                tag = match.group("tag")
948
                if tag in doc.information:
949
                    ret = Link(
950
                        doc.information[tag].link,
951
                        url="#" + tag,
952
                        title=doc.information[tag].caption.replace(
953
                            "%c", str(doc.count[doc.information[tag].category])
954
                        ),
955
                    )
956
                    replace_count(ret, str(doc.count[doc.information[tag].category]))
957
                    return ret
958
    return None
959
960
961
def update_header_numbers(elem, doc):
962
    """
963
    Update header numbers.
964
965
    Arguments
966
    ---------
967
    elem
968
        element to update
969
    doc
970
        pandoc document
971
    """
972
    if "unnumbered" not in elem.classes:
973
        doc.headers[elem.level - 1] = doc.headers[elem.level - 1] + 1
974
        for index in range(elem.level, 6):
975
            doc.headers[index] = 0
976
977
978
def update_header_aliases(elem, doc):
979
    """
980
    Update header aliases.
981
982
    Arguments
983
    ---------
984
    elem
985
        element to update
986
    doc
987
        pandoc document
988
    """
989
    doc.aliases[elem.level - 1] = elem.identifier
990
    for index in range(elem.level, 6):
991
        doc.aliases[index] = ""
992
993
994
def prepare(doc):
995
    """
996
    Prepare document.
997
998
    Arguments
999
    ---------
1000
    doc
1001
        pandoc document
1002
    """
1003
    doc.headers = [0, 0, 0, 0, 0, 0]
1004
    doc.aliases = ["", "", "", "", "", ""]
1005
    doc.information = {}
1006
    doc.defined = {}
1007
1008
    if "pandoc-numbering" in doc.metadata.content and isinstance(
1009
        doc.metadata.content["pandoc-numbering"], MetaMap
1010
    ):
1011
        for category, definition in doc.metadata.content[
1012
            "pandoc-numbering"
1013
        ].content.items():
1014
            if isinstance(definition, MetaMap):
1015
                add_definition(category, definition, doc)
1016
1017
    doc.count = {}
1018
    doc.collections = {}
1019
1020
1021
def add_definition(category, definition, doc):
1022
    """
1023
    Add definition for a category.
1024
1025
    Arguments
1026
    ---------
1027
    category
1028
        The category
1029
    definition
1030
        The definition
1031
    doc
1032
        The pandoc document
1033
    """
1034
    # Create the category with options by default
1035
    define(category, doc)
1036
1037
    # Detect general options
1038
    if "general" in definition:
1039
        meta_cite(category, definition["general"], doc.defined)
1040
        meta_listing(category, definition["general"], doc.defined)
1041
        meta_levels(category, definition["general"], doc.defined)
1042
        meta_classes(category, definition["general"], doc.defined)
1043
1044
    # Detect LaTeX options
1045
    if doc.format in {"tex", "latex"}:
1046
        if "latex" in definition:
1047
            meta_format_text(category, definition["latex"], doc.defined)
1048
            meta_format_link(category, definition["latex"], doc.defined)
1049
            meta_format_caption(category, definition["latex"], doc.defined)
1050
            meta_format_entry(category, definition["latex"], doc.defined)
1051
            meta_entry_tab(category, definition["latex"], doc.defined)
1052
            meta_entry_space(category, definition["latex"], doc.defined)
1053
    # Detect standard options
1054
    else:
1055
        if "standard" in definition:
1056
            meta_format_text(category, definition["standard"], doc.defined)
1057
            meta_format_link(category, definition["standard"], doc.defined)
1058
            meta_format_caption(category, definition["standard"], doc.defined)
1059
            meta_format_entry(category, definition["standard"], doc.defined)
1060
1061
1062
def meta_cite(category, definition, defined):
1063
    """
1064
    Compute cite for a category.
1065
1066
    Arguments
1067
    ---------
1068
    category
1069
        The category
1070
    definition
1071
        The definition
1072
    defined
1073
        The defined parameter
1074
    """
1075
    if "cite-shortcut" in definition:
1076
        if isinstance(definition["cite-shortcut"], MetaBool):
1077
            defined[category]["cite-shortcut"] = definition["cite-shortcut"].boolean
1078
        else:
1079
            debug(
1080
                "[WARNING] pandoc-numbering: cite-shortcut is not correct for category "
1081
                + category
1082
            )
1083
1084
1085
# pylint:disable=too-many-branches
1086
def meta_listing(category, definition, defined):
1087
    """
1088
    Compute listing for a category.
1089
1090
    Arguments
1091
    ---------
1092
    category
1093
        The category
1094
    definition
1095
        The definition
1096
    defined
1097
        The defined parameter
1098
    """
1099
    if "listing-title" in definition:
1100
        if isinstance(definition["listing-title"], MetaInlines):
1101
            defined[category]["listing-title"] = definition["listing-title"].content
1102
            # Detach from original parent
1103
            defined[category]["listing-title"].parent = None
1104
        else:
1105
            debug(
1106
                "[WARNING] pandoc-numbering: listing-title is not correct for category "
1107
                + category
1108
            )
1109 View Code Duplication
    if "listing-unnumbered" in definition:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1110
        if isinstance(definition["listing-unnumbered"], MetaBool):
1111
            defined[category]["listing-unnumbered"] = definition[
1112
                "listing-unnumbered"
1113
            ].boolean
1114
        else:
1115
            debug(
1116
                "[WARNING] pandoc-numbering: "
1117
                "listing-unnumbered is not correct for category " + category
1118
            )
1119 View Code Duplication
    if "listing-unlisted" in definition:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1120
        if isinstance(definition["listing-unlisted"], MetaBool):
1121
            defined[category]["listing-unlisted"] = definition[
1122
                "listing-unlisted"
1123
            ].boolean
1124
        else:
1125
            debug(
1126
                "[WARNING] pandoc-numbering: "
1127
                "listing-unlisted is not correct for category " + category
1128
            )
1129
    if "listing-identifier" in definition:
1130
        if isinstance(definition["listing-identifier"], MetaBool):
1131
            defined[category]["listing-identifier"] = definition[
1132
                "listing-identifier"
1133
            ].boolean
1134
        elif (
1135
            isinstance(definition["listing-identifier"], MetaInlines)
1136
            and len(definition["listing-identifier"].content) == 1
1137
            and isinstance(definition["listing-identifier"].content[0], Str)
1138
        ):
1139
            defined[category]["listing-identifier"] = (
1140
                definition["listing-identifier"].content[0].text
1141
            )
1142
        else:
1143
            debug(
1144
                "[WARNING] pandoc-numbering: "
1145
                "listing-identifier is not correct for category " + category
1146
            )
1147
1148
1149 View Code Duplication
def meta_format_text(category, definition, defined):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1150
    """
1151
    Compute format text for a category.
1152
1153
    Arguments
1154
    ---------
1155
    category
1156
        The category
1157
    definition
1158
        The definition
1159
    defined
1160
        The defined parameter
1161
    """
1162
    if "format-text-classic" in definition:
1163
        if isinstance(definition["format-text-classic"], MetaInlines):
1164
            defined[category]["format-text-classic"] = definition[
1165
                "format-text-classic"
1166
            ].content
1167
            # Detach from original parent
1168
            defined[category]["format-text-classic"].parent = None
1169
        else:
1170
            debug(
1171
                "[WARNING] pandoc-numbering: "
1172
                "format-text-classic is not correct for category " + category
1173
            )
1174
1175
    if "format-text-title" in definition:
1176
        if isinstance(definition["format-text-title"], MetaInlines):
1177
            defined[category]["format-text-title"] = definition[
1178
                "format-text-title"
1179
            ].content
1180
            # Detach from original parent
1181
            defined[category]["format-text-title"].parent = None
1182
        else:
1183
            debug(
1184
                "[WARNING] pandoc-numbering: "
1185
                "format-text-title is not correct for category " + category
1186
            )
1187
1188
1189 View Code Duplication
def meta_format_link(category, definition, defined):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1190
    """
1191
    Compute format link for a category.
1192
1193
    Arguments
1194
    ---------
1195
    category
1196
        The category
1197
    definition
1198
        The definition
1199
    defined
1200
        The defined parameter
1201
    """
1202
    if "format-link-classic" in definition:
1203
        if isinstance(definition["format-link-classic"], MetaInlines):
1204
            defined[category]["format-link-classic"] = definition[
1205
                "format-link-classic"
1206
            ].content
1207
            # Detach from original parent
1208
            defined[category]["format-link-classic"].parent = None
1209
        else:
1210
            debug(
1211
                "[WARNING] pandoc-numbering: "
1212
                "format-link-classic is not correct for category " + category
1213
            )
1214
1215
    if "format-link-title" in definition:
1216
        if isinstance(definition["format-link-title"], MetaInlines):
1217
            defined[category]["format-link-title"] = definition[
1218
                "format-link-title"
1219
            ].content
1220
            # Detach from original parent
1221
            defined[category]["format-link-title"].parent = None
1222
        else:
1223
            debug(
1224
                "[WARNING] pandoc-numbering: "
1225
                "format-link-title is not correct for category " + category
1226
            )
1227
1228
1229
def meta_format_caption(category, definition, defined):
1230
    """
1231
    Compute format caption for a category.
1232
1233
    Arguments
1234
    ---------
1235
    category
1236
        The category
1237
    definition
1238
        The definition
1239
    defined
1240
        The defined parameter
1241
    """
1242 View Code Duplication
    if "format-caption-classic" in definition:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1243
        if isinstance(definition["format-caption-classic"], MetaInlines):
1244
            defined[category]["format-caption-classic"] = stringify(
1245
                definition["format-caption-classic"]
1246
            )
1247
        else:
1248
            debug(
1249
                "[WARNING] pandoc-numbering: "
1250
                "format-caption-classic is not correct for category " + category
1251
            )
1252
1253 View Code Duplication
    if "format-caption-title" in definition:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1254
        if isinstance(definition["format-caption-title"], MetaInlines):
1255
            defined[category]["format-caption-title"] = stringify(
1256
                definition["format-caption-title"]
1257
            )
1258
        else:
1259
            debug(
1260
                "[WARNING] pandoc-numbering: "
1261
                "format-caption-title is not correct for category " + category
1262
            )
1263
1264
1265 View Code Duplication
def meta_format_entry(category, definition, defined):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1266
    """
1267
    Compute format entry for a category.
1268
1269
    Arguments
1270
    ---------
1271
    category
1272
        The category
1273
    definition
1274
        The definition
1275
    defined
1276
        The defined parameter
1277
    """
1278
    if "format-entry-classic" in definition:
1279
        if isinstance(definition["format-entry-classic"], MetaInlines):
1280
            defined[category]["format-entry-classic"] = definition[
1281
                "format-entry-classic"
1282
            ].content
1283
            # Detach from original parent
1284
            defined[category]["format-entry-classic"].parent = None
1285
        else:
1286
            debug(
1287
                "[WARNING] pandoc-numbering: "
1288
                "format-entry-classic is not correct for category " + category
1289
            )
1290
1291
    if "format-entry-title" in definition:
1292
        if isinstance(definition["format-entry-title"], MetaInlines):
1293
            defined[category]["format-entry-title"] = definition[
1294
                "format-entry-title"
1295
            ].content
1296
            # Detach from original parent
1297
            defined[category]["format-entry-title"].parent = None
1298
        else:
1299
            debug(
1300
                "[WARNING] pandoc-numbering: "
1301
                "format-entry-title is not correct for category " + category
1302
            )
1303
1304
1305 View Code Duplication
def meta_entry_tab(category, definition, defined):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1306
    """
1307
    Compute entry tab for a category.
1308
1309
    Arguments
1310
    ---------
1311
    category
1312
        The category
1313
    definition
1314
        The definition
1315
    defined
1316
        The defined parameter
1317
    """
1318
    if "entry-tab" in definition:
1319
        if isinstance(definition["entry-tab"], MetaString):
1320
            value = definition["entry-tab"].text
1321
        elif (
1322
            isinstance(definition["entry-tab"], MetaInlines)
1323
            and len(definition["entry-tab"].content) == 1
1324
        ):
1325
            value = definition["entry-tab"].content[0].text
1326
        else:
1327
            debug(
1328
                "[WARNING] pandoc-numbering: entry-tab is not correct for category "
1329
                + category
1330
            )
1331
            return
1332
        # Get the tab
1333
        try:
1334
            tab = float(value)
1335
            if tab > 0:
1336
                defined[category]["entry-tab"] = tab
1337
            else:
1338
                debug(
1339
                    "[WARNING] pandoc-numbering: "
1340
                    "entry-tab must be positive for category " + category
1341
                )
1342
        except ValueError:
1343
            debug(
1344
                "[WARNING] pandoc-numbering: entry-tab is not correct for category "
1345
                + category
1346
            )
1347
1348
1349 View Code Duplication
def meta_entry_space(category, definition, defined):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1350
    """
1351
    Compute entry space for a category.
1352
1353
    Arguments
1354
    ---------
1355
    category
1356
        The category
1357
    definition
1358
        The definition
1359
    defined
1360
        The defined parameter
1361
    """
1362
    if "entry-space" in definition:
1363
        if isinstance(definition["entry-space"], MetaString):
1364
            value = definition["entry-space"].text
1365
        elif (
1366
            isinstance(definition["entry-space"], MetaInlines)
1367
            and len(definition["entry-space"].content) == 1
1368
        ):
1369
            value = definition["entry-space"].content[0].text
1370
        else:
1371
            debug(
1372
                "[WARNING] pandoc-numbering: entry-space is not correct for category "
1373
                + category
1374
            )
1375
            return
1376
        # Get the space
1377
        try:
1378
            space = float(value)
1379
            if space > 0:
1380
                defined[category]["entry-space"] = space
1381
            else:
1382
                debug(
1383
                    "[WARNING] pandoc-numbering: "
1384
                    "entry-space must be positive for category " + category
1385
                )
1386
        except ValueError:
1387
            debug(
1388
                "[WARNING] pandoc-numbering: entry-space is not correct for category "
1389
                + category
1390
            )
1391
1392
1393
def meta_levels(category, definition, defined):
1394
    """
1395
    Compute level for a category.
1396
1397
    Arguments
1398
    ---------
1399
    category
1400
        The category
1401
    definition
1402
        The definition
1403
    defined
1404
        The defined parameter
1405
    """
1406
    if (
1407
        "sectioning-levels" in definition
1408
        and isinstance(definition["sectioning-levels"], MetaInlines)
1409
        and len(definition["sectioning-levels"].content) == 1
1410
    ):
1411
        match = re.match(
1412
            Numbered.header_regex, definition["sectioning-levels"].content[0].text
1413
        )
1414
        if match:
1415
            # Compute the first and last levels section
1416
            defined[category]["first-section-level"] = len(match.group("hidden")) // 2
1417
            defined[category]["last-section-level"] = len(match.group("header")) // 2
1418
    if "first-section-level" in definition:
1419
        if isinstance(definition["first-section-level"], MetaString):
1420
            value = definition["first-section-level"].text
1421
        elif (
1422
            isinstance(definition["first-section-level"], MetaInlines)
1423
            and len(definition["first-section-level"].content) == 1
1424
        ):
1425
            value = definition["first-section-level"].content[0].text
1426
        else:
1427
            debug(
1428
                "[WARNING] pandoc-numbering: "
1429
                "first-section-level is not correct for category " + category
1430
            )
1431
            return
1432
1433
        # Get the level
1434
        try:
1435
            level = int(value) - 1
1436
        except ValueError:
1437
            debug(
1438
                "[WARNING] pandoc-numbering: "
1439
                "first-section-level is not correct for category " + category
1440
            )
1441
1442
        if 0 <= level <= 6:
1443
            defined[category]["first-section-level"] = level
1444
        else:
1445
            # pylint: disable=line-too-long
1446
            debug(
1447
                "[WARNING] pandoc-numbering: "
1448
                "first-section-level must be positive or zero for category " + category
1449
            )
1450
1451
    if "last-section-level" in definition:
1452
        if isinstance(definition["last-section-level"], MetaString):
1453
            value = definition["last-section-level"].text
1454
        elif (
1455
            isinstance(definition["last-section-level"], MetaInlines)
1456
            and len(definition["last-section-level"].content) == 1
1457
        ):
1458
            value = definition["last-section-level"].content[0].text
1459
        else:
1460
            debug(
1461
                "[WARNING] pandoc-numbering: "
1462
                "last-section-level is not correct for category " + category
1463
            )
1464
            return
1465
1466
        # Get the level
1467
        try:
1468
            level = int(value)
1469
        except ValueError:
1470
            debug(
1471
                "[WARNING] pandoc-numbering: "
1472
                "last-section-level is not correct for category " + category
1473
            )
1474
1475
        if 0 <= level <= 6:
1476
            defined[category]["last-section-level"] = level
1477
        else:
1478
            # pylint: disable=line-too-long
1479
            debug(
1480
                "[WARNING] pandoc-numbering: "
1481
                "last-section-level must be positive or zero for category " + category
1482
            )
1483
1484
1485
def meta_classes(category, definition, defined):
1486
    """
1487
    Compute classes for a category.
1488
1489
    Arguments
1490
    ---------
1491
    category
1492
        The category
1493
    definition
1494
        The definition
1495
    defined
1496
        The defined parameter
1497
    """
1498
    if "classes" in definition and isinstance(definition["classes"], MetaList):
1499
        defined[category]["classes"] = [
1500
            stringify(elt) for elt in definition["classes"].content
1501
        ]
1502
1503
1504
def finalize(doc):
1505
    """
1506
    Finalize document.
1507
1508
    Arguments
1509
    ---------
1510
    doc
1511
        The pandoc document
1512
    """
1513
    # Loop on all listings definition
1514
1515
    if doc.format in {"tex", "latex"}:
1516
        # Add header-includes if necessary
1517
        if "header-includes" not in doc.metadata:
1518
            doc.metadata["header-includes"] = MetaList()
1519
        # Convert header-includes to MetaList if necessary
1520
        elif not isinstance(doc.metadata["header-includes"], MetaList):
1521
            doc.metadata["header-includes"] = MetaList(doc.metadata["header-includes"])
1522
1523
        doc.metadata["header-includes"].append(
1524
            MetaInlines(
1525
                RawInline(
1526
                    dedent(
1527
                        r"""
1528
                        \makeatletter
1529
                        \@ifpackageloaded{subfig}{
1530
                            \usepackage[subfigure]{tocloft}
1531
                        }{
1532
                            \usepackage{tocloft}
1533
                        }
1534
                        \makeatother
1535
                        """
1536
                    ),
1537
                    "tex",
1538
                )
1539
            )
1540
        )
1541
        doc.metadata["header-includes"].append(
1542
            MetaInlines(RawInline(r"\usepackage{etoolbox}", "tex"))
1543
        )
1544
1545
    i = 0
1546
    listof = []
1547
    for category, definition in doc.defined.items():
1548
        if definition["listing-title"] is not None:
1549
            # pylint: disable=consider-using-f-string
1550
            if doc.format in {"tex", "latex"}:
1551
                latex_category = re.sub("[^a-z]+", "", category)
1552
                latex = (
1553
                    r"\newlistof{%s}{%s}{%s}"
1554
                    r"\renewcommand{\cft%stitlefont}{\cfttoctitlefont}"
1555
                    r"\setlength{\cft%snumwidth}{\cftfignumwidth}"
1556
                    r"\setlength{\cft%sindent}{\cftfigindent}"
1557
                    % (
1558
                        latex_category,
1559
                        latex_category,
1560
                        convert_text(
1561
                            Plain(*definition["listing-title"]),
1562
                            input_format="panflute",
1563
                            output_format="latex",
1564
                        ),
1565
                        latex_category,
1566
                        latex_category,
1567
                        latex_category,
1568
                    )
1569
                )
1570
                doc.metadata["header-includes"].append(
1571
                    MetaInlines(RawInline(latex, "tex"))
1572
                )
1573
                listof.append(f"\\listof{latex_category}")
1574
            else:
1575
                classes = ["pandoc-numbering-listing"] + definition["classes"]
1576
1577
                if definition["listing-unnumbered"]:
1578
                    classes.append("unnumbered")
1579
1580
                if definition["listing-unlisted"]:
1581
                    classes.append("unlisted")
1582
1583
                if definition["listing-identifier"] is False:
1584
                    header = Header(
1585
                        *definition["listing-title"], level=1, classes=classes
1586
                    )
1587
                elif definition["listing-identifier"] is True:
1588
                    header = Header(
1589
                        *definition["listing-title"], level=1, classes=classes
1590
                    )
1591
                    header = convert_text(
1592
                        convert_text(
1593
                            header, input_format="panflute", output_format="markdown"
1594
                        ),
1595
                        output_format="panflute",
1596
                    )[0]
1597
                else:
1598
                    header = Header(
1599
                        *definition["listing-title"],
1600
                        level=1,
1601
                        classes=classes,
1602
                        identifier=definition["listing-identifier"],
1603
                    )
1604
1605
                doc.content.insert(i, header)
1606
                i = i + 1
1607
1608
                table = table_other(doc, category, definition)
1609
1610
                if table:
1611
                    doc.content.insert(i, table)
1612
                    i = i + 1
1613
1614
    if doc.format in {"tex", "latex"}:
1615
        header = (
1616
            r"\ifdef{\mainmatter}"
1617
            r"{\let\oldmainmatter\mainmatter"
1618
            r"\renewcommand{\mainmatter}[0]{%s\oldmainmatter}}"
1619
            r"{}"
1620
        )
1621
        doc.metadata["header-includes"].append(
1622
            MetaInlines(RawInline(header % "\n".join(listof), "tex"))
1623
        )
1624
1625
        latex = r"\ifdef{\mainmatter}{}{%s}"
1626
        doc.content.insert(0, RawBlock(latex % "\n".join(listof), "tex"))
1627
1628
1629
def table_other(doc, category, _):
1630
    """
1631
    Compute other code for table.
1632
1633
    Arguments
1634
    ---------
1635
    doc
1636
        pandoc document
1637
    category
1638
        category numbered
1639
1640
    Returns
1641
    -------
1642
        A BulletList or None
1643
    """
1644
    if category in doc.collections:
1645
        # Return a bullet list
1646
        return BulletList(
1647
            *(
1648
                ListItem(Plain(Link(doc.information[tag].entry, url="#" + tag)))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable tag does not seem to be defined.
Loading history...
1649
                for tag in doc.collections[category]
1650
            )
1651
        )
1652
    return None
1653
1654
1655
def link_color(doc):
1656
    """
1657
    Compute LaTeX code for toc.
1658
1659
    Arguments
1660
    ---------
1661
    doc
1662
        pandoc document
1663
1664
    Returns
1665
    -------
1666
        LaTeX code for links.
1667
    """
1668
    # Get the link color
1669
    metadata = doc.get_metadata()
1670
    if "toccolor" in metadata:
1671
        return "\\hypersetup{linkcolor=" + str(metadata["toccolor"]) + "}"
1672
    return "\\hypersetup{linkcolor=black}"
1673
1674
1675
def main(doc=None) -> None:
1676
    """
1677
    Produce the final document.
1678
1679
    Parameters
1680
    ----------
1681
    doc
1682
        pandoc document
1683
    """
1684
    run_filters([numbering, referencing], prepare=prepare, doc=doc, finalize=finalize)
1685
1686
1687
if __name__ == "__main__":
1688
    main()
1689