1 | 1 | from abc import ABC |
|
2 | 1 | from typing import Any, Dict |
|
3 | |||
4 | 1 | from cleo.io.io import IO |
|
5 | |||
6 | 1 | import sdoc |
|
7 | 1 | from sdoc.sdoc2 import in_scope, out_scope |
|
8 | 1 | from sdoc.sdoc2.helper.Enumerable import Enumerable |
|
9 | 1 | from sdoc.sdoc2.node.EndParagraphNode import EndParagraphNode |
|
10 | 1 | from sdoc.sdoc2.node.Node import Node |
|
11 | 1 | from sdoc.sdoc2.node.TextNode import TextNode |
|
12 | 1 | from sdoc.sdoc2.NodeStore import NodeStore |
|
13 | |||
14 | |||
15 | 1 | View Code Duplication | class HeadingNode(Node, ABC): |
0 ignored issues
–
show
Duplication
introduced
by
![]() |
|||
16 | """ |
||
17 | Abstract class for heading nodes. |
||
18 | """ |
||
19 | |||
20 | # ------------------------------------------------------------------------------------------------------------------ |
||
21 | 1 | def __init__(self, io: IO, name: str, options: Dict[str, str], argument: str): |
|
22 | """ |
||
23 | Object constructor. |
||
24 | |||
25 | :param OutputStyle io: The IO object. |
||
26 | :param str name: The (command) name of this heading. |
||
27 | :param dict[str,str] options: The options of this heading. |
||
28 | :param str argument: The title of this heading. |
||
29 | """ |
||
30 | 1 | super().__init__(io, name, options, argument) |
|
31 | |||
32 | 1 | self.numbering: bool = True |
|
33 | 1 | """ |
|
34 | The True the node must be numbered. |
||
35 | """ |
||
36 | |||
37 | # ------------------------------------------------------------------------------------------------------------------ |
||
38 | 1 | def get_hierarchy_name(self) -> str: |
|
39 | """ |
||
40 | Returns 'sectioning'. |
||
41 | """ |
||
42 | 1 | return 'sectioning' |
|
43 | |||
44 | # ------------------------------------------------------------------------------------------------------------------ |
||
45 | 1 | def is_block_command(self) -> bool: |
|
46 | """ |
||
47 | Returns False. |
||
48 | """ |
||
49 | 1 | return False |
|
50 | |||
51 | # ------------------------------------------------------------------------------------------------------------------ |
||
52 | 1 | def is_inline_command(self) -> bool: |
|
53 | """ |
||
54 | Returns True. |
||
55 | """ |
||
56 | return True |
||
57 | |||
58 | # ------------------------------------------------------------------------------------------------------------------ |
||
59 | 1 | def number(self, enumerable_numbers: Dict[str, Any]): |
|
60 | """ |
||
61 | Sets number of heading nodes. |
||
62 | |||
63 | :param dict[str,any] enumerable_numbers: |
||
64 | """ |
||
65 | 1 | if 'heading' not in enumerable_numbers: |
|
66 | 1 | enumerable_numbers['heading'] = Enumerable() |
|
67 | |||
68 | 1 | enumerable_numbers['heading'].generate_numeration(self.get_hierarchy_level()) |
|
69 | 1 | enumerable_numbers['heading'].increment_last_level() |
|
70 | 1 | enumerable_numbers['heading'].remove_starting_zeros() |
|
71 | |||
72 | 1 | if 'part' in enumerable_numbers: |
|
73 | self._options['part_number'] = enumerable_numbers['part'].get_string() |
||
74 | |||
75 | 1 | self._options['number'] = enumerable_numbers['heading'].get_string() |
|
76 | |||
77 | 1 | super().number(enumerable_numbers) |
|
78 | |||
79 | # ------------------------------------------------------------------------------------------------------------------ |
||
80 | 1 | def set_toc_id(self) -> None: |
|
81 | """ |
||
82 | Set ID for table of contents. |
||
83 | """ |
||
84 | if 'id' not in self._options: |
||
85 | if 'part_number' in self._options: |
||
86 | self._options['id'] = '{}:{}:{}'.format(self.name, |
||
87 | self._options['part_number'], |
||
88 | self._options['number']) |
||
89 | else: |
||
90 | self._options['id'] = '{}:{}'.format(self.name, self._options['number']) |
||
91 | |||
92 | # ------------------------------------------------------------------------------------------------------------------ |
||
93 | 1 | def prepare_content_tree(self) -> None: |
|
94 | """ |
||
95 | Prepares the content tree. Create paragraph nodes. |
||
96 | """ |
||
97 | 1 | super().prepare_content_tree() |
|
98 | |||
99 | 1 | self.set_numbering() |
|
100 | |||
101 | # Adding the id's of split text in 'new_child_nodes1' list. |
||
102 | 1 | self.split_text_nodes() |
|
103 | |||
104 | # Creating paragraphs and add all id's in 'new_child_nodes2' list. |
||
105 | 1 | self.create_paragraphs() |
|
106 | |||
107 | # ------------------------------------------------------------------------------------------------------------------ |
||
108 | 1 | def set_numbering(self) -> None: |
|
109 | """ |
||
110 | Sets the numbering status to the heading node. |
||
111 | """ |
||
112 | 1 | if 'numbering' in self._options: |
|
113 | if self._options['numbering'] == 'off': |
||
114 | self.numbering = False |
||
115 | elif self._options['numbering'] == 'on': |
||
116 | self.numbering = True |
||
117 | else: |
||
118 | NodeStore.error("Invalid value '{}' for attribute 'numbering'. Allowed values are 'on' and 'off'.". |
||
119 | format(self._options['numbering']), self) |
||
120 | |||
121 | # ------------------------------------------------------------------------------------------------------------------ |
||
122 | 1 | def split_text_nodes(self) -> None: |
|
123 | """ |
||
124 | Replaces single text nodes that contains a paragraph separator (i.e. a double new line) with multiple text nodes |
||
125 | without paragraph separator. |
||
126 | """ |
||
127 | 1 | new_child_nodes = [] |
|
128 | |||
129 | 1 | for node_id in self.child_nodes: |
|
130 | 1 | node = in_scope(node_id) |
|
131 | |||
132 | 1 | if isinstance(node, TextNode): |
|
133 | 1 | list_ids = node.split_by_paragraph() |
|
134 | 1 | for ids in list_ids: |
|
135 | 1 | new_child_nodes.append(ids) |
|
136 | else: |
||
137 | 1 | new_child_nodes.append(node.id) |
|
138 | |||
139 | 1 | out_scope(node) |
|
140 | |||
141 | 1 | self.child_nodes = new_child_nodes |
|
142 | |||
143 | # ------------------------------------------------------------------------------------------------------------------ |
||
144 | 1 | def create_paragraphs(self) -> None: |
|
145 | """ |
||
146 | Create paragraph nodes. |
||
147 | |||
148 | A paragraph consists of phrasing nodes only. Each continuous slice of phrasing child nodes is move to a |
||
149 | paragraph node. |
||
150 | """ |
||
151 | 1 | new_child_nodes = [] |
|
152 | 1 | paragraph_node = None |
|
153 | |||
154 | 1 | for node_id in self.child_nodes: |
|
155 | 1 | node = in_scope(node_id) |
|
156 | |||
157 | 1 | if node.is_phrasing(): |
|
158 | 1 | if not paragraph_node: |
|
159 | 1 | paragraph_node = sdoc.sdoc2.node_store.create_inline_node('paragraph') |
|
160 | 1 | new_child_nodes.append(paragraph_node.id) |
|
161 | |||
162 | 1 | paragraph_node.append_child_node(node) |
|
163 | else: |
||
164 | 1 | if paragraph_node: |
|
165 | 1 | paragraph_node.prune_whitespace() |
|
166 | 1 | sdoc.sdoc2.node_store.store_node(paragraph_node) |
|
167 | 1 | paragraph_node = None |
|
168 | |||
169 | # End paragraph nodes are created temporary to separate paragraphs in a flat list of (text) node. There |
||
170 | # role ae replaced by the content hierarchy now. So, we must no store end paragraph nodes. |
||
171 | 1 | if not isinstance(node, EndParagraphNode): |
|
172 | 1 | new_child_nodes.append(node.id) |
|
173 | |||
174 | 1 | out_scope(node) |
|
175 | |||
176 | 1 | if paragraph_node: |
|
177 | 1 | paragraph_node.prune_whitespace() |
|
178 | 1 | sdoc.sdoc2.node_store.store_node(paragraph_node) |
|
179 | # paragraph_node = None |
||
180 | |||
181 | # Setting child nodes. |
||
182 | 1 | self.child_nodes = new_child_nodes |
|
183 | |||
184 | # ---------------------------------------------------------------------------------------------------------------------- |
||
185 |