Almirah 0.2.4 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/bin/almirah +4 -4
  3. data/lib/almirah/doc_fabric.rb +65 -65
  4. data/lib/almirah/doc_items/blockquote.rb +21 -21
  5. data/lib/almirah/doc_items/code_block.rb +26 -26
  6. data/lib/almirah/doc_items/controlled_paragraph.rb +112 -112
  7. data/lib/almirah/doc_items/controlled_table.rb +224 -224
  8. data/lib/almirah/doc_items/controlled_table_row.rb +22 -22
  9. data/lib/almirah/doc_items/doc_footer.rb +16 -16
  10. data/lib/almirah/doc_items/doc_item.rb +22 -20
  11. data/lib/almirah/doc_items/frontmatter.rb +9 -0
  12. data/lib/almirah/doc_items/heading.rb +93 -93
  13. data/lib/almirah/doc_items/image.rb +27 -27
  14. data/lib/almirah/doc_items/markdown_list.rb +156 -158
  15. data/lib/almirah/doc_items/markdown_table.rb +61 -63
  16. data/lib/almirah/doc_items/paragraph.rb +25 -27
  17. data/lib/almirah/doc_items/text_line.rb +296 -296
  18. data/lib/almirah/doc_items/todo_block.rb +21 -21
  19. data/lib/almirah/doc_parser.rb +378 -330
  20. data/lib/almirah/doc_types/base_document.rb +64 -70
  21. data/lib/almirah/doc_types/coverage.rb +95 -81
  22. data/lib/almirah/doc_types/index.rb +173 -169
  23. data/lib/almirah/doc_types/persistent_document.rb +17 -20
  24. data/lib/almirah/doc_types/protocol.rb +24 -24
  25. data/lib/almirah/doc_types/specification.rb +67 -67
  26. data/lib/almirah/doc_types/traceability.rb +142 -142
  27. data/lib/almirah/dom/doc_section.rb +25 -25
  28. data/lib/almirah/dom/document.rb +78 -72
  29. data/lib/almirah/navigation_pane.rb +16 -16
  30. data/lib/almirah/project.rb +287 -306
  31. data/lib/almirah/project_configuration.rb +41 -41
  32. data/lib/almirah/project_template.rb +298 -0
  33. data/lib/almirah/project_utility.rb +52 -0
  34. data/lib/almirah/search/specifications_db.rb +79 -83
  35. data/lib/almirah/templates/css/main.css +300 -300
  36. data/lib/almirah/templates/css/search.css +40 -40
  37. data/lib/almirah/templates/page.html +42 -42
  38. data/lib/almirah/templates/scripts/main.js +111 -111
  39. data/lib/almirah/templates/scripts/orama_search.js +138 -138
  40. data/lib/almirah.rb +93 -49
  41. metadata +28 -5
@@ -1,330 +1,378 @@
1
- require_relative 'doc_items/text_line'
2
- require_relative 'doc_items/doc_item'
3
- require_relative 'doc_items/heading'
4
- require_relative 'doc_items/paragraph'
5
- require_relative 'doc_items/blockquote'
6
- require_relative 'doc_items/code_block'
7
- require_relative 'doc_items/todo_block'
8
- require_relative 'doc_items/controlled_paragraph'
9
- require_relative 'doc_items/markdown_table'
10
- require_relative 'doc_items/controlled_table'
11
- require_relative 'doc_items/image'
12
- require_relative 'doc_items/markdown_list'
13
- require_relative 'doc_items/doc_footer'
14
-
15
- class DocParser
16
- def self.parse(doc, text_lines)
17
- temp_md_table = nil
18
- temp_md_list = nil
19
- temp_code_block = nil
20
- # restart section numbering for each new document
21
- Heading.reset_global_section_number
22
-
23
- text_lines.each do |s|
24
- if s.lstrip != ''
25
- if res = /^(\#{1,})\s(.*)/.match(s) # Heading
26
-
27
- temp_md_table = process_temp_table(doc, temp_md_table)
28
- if temp_md_list
29
- doc.items.append temp_md_list
30
- temp_md_list = nil
31
- end
32
-
33
- level = res[1].length
34
- value = res[2]
35
-
36
- if level == 1 && doc.title == ''
37
- doc.title = value
38
- end
39
-
40
- item = Heading.new(doc, value, level)
41
- doc.items.append(item)
42
- doc.headings.append(item)
43
-
44
- elsif res = /^%\s(.*)/.match(s) # Pandoc Document Title
45
-
46
- title = res[1]
47
-
48
- if doc.title == ''
49
- doc.title = title
50
- end
51
-
52
- item = Heading.new(doc, title, 0)
53
- doc.items.append(item)
54
- doc.headings.append(item)
55
-
56
- elsif res = /^\[(\S*)\]\s+(.*)/.match(s) # Controlled Paragraph
57
-
58
- temp_md_table = process_temp_table(doc, temp_md_table)
59
- if temp_md_list
60
- doc.items.append temp_md_list
61
- temp_md_list = nil
62
- end
63
-
64
- id = res[1].upcase
65
- text = res[2]
66
- up_links = nil
67
-
68
- # check if it contains the uplink (one or many)
69
- # TODO: check this regular expression
70
- first_pos = text.length # for trailing commas
71
- tmp = text.scan(/(>\[(?>[^\[\]]|\g<0>)*\])/) # >[SRS-001], >[SYS-002]
72
- if tmp.length > 0
73
- up_links = []
74
- tmp.each do |ul|
75
- lnk = ul[0]
76
- # do not add links for the self document
77
- doc_id = /([a-zA-Z]+)-\d+/.match(lnk) # SRS
78
- if (doc_id) and (doc_id[1].downcase != doc.id.downcase)
79
- up_links << lnk.upcase
80
- end
81
- # try to find the real end of text
82
- pos = text.index(lnk)
83
- first_pos = pos if pos < first_pos
84
- # remove uplink from text
85
- text = text.split(lnk, 1).join('')
86
- end
87
- # remove trailing commas and spaces
88
- if text.length > first_pos
89
- first_pos -= 1
90
- text = text[0..first_pos].strip
91
- end
92
- end
93
-
94
- # since we already know id and text
95
- item = ControlledParagraph.new(doc, text, id)
96
-
97
- if up_links
98
- up_links.uniq! #remove duplicates
99
- doc.items_with_uplinks_number += 1 # for statistics
100
- up_links.each do |ul|
101
- next unless tmp = />\[(\S*)\]$/.match(ul) # >[SRS-001]
102
-
103
- up_link_id = tmp[1]
104
-
105
- item.up_link_ids = [] unless item.up_link_ids
106
-
107
- item.up_link_ids.append(up_link_id)
108
-
109
- if tmp = /^([a-zA-Z]+)-\d+/.match(up_link_id) # SRS
110
- doc.up_link_docs[tmp[1].downcase.to_s] = tmp[1].downcase # multiple documents could be up-linked
111
- end
112
- end
113
- end
114
-
115
- doc.items.append(item)
116
- # for statistics
117
- if doc.dictionary.has_key?(id.to_s)
118
- doc.duplicated_ids_number += 1
119
- doc.duplicates_list.append(item)
120
- else
121
- doc.dictionary[id.to_s] = item # for fast search
122
- end
123
- doc.controlled_items.append(item) # for fast search
124
-
125
- # for statistics
126
- n = /\d+/.match(id)[0].to_i
127
- if n > doc.last_used_id_number
128
- doc.last_used_id = id
129
- doc.last_used_id_number = n
130
- end
131
-
132
- elsif res = /^!\[(.*)\]\((.*)\)/.match(s) # Image
133
-
134
- temp_md_table = process_temp_table(doc, temp_md_table)
135
- if temp_md_list
136
- doc.items.append temp_md_list
137
- temp_md_list = nil
138
- end
139
-
140
- img_text = res[1]
141
- img_path = res[2]
142
-
143
- item = Image.new(img_text, img_path)
144
- item.parent_doc = doc
145
- item.parent_heading = doc.headings[-1]
146
-
147
- doc.items.append(item)
148
-
149
- elsif res = /^(\*\s+)(.*)/.match(s) # check if unordered list start
150
-
151
- temp_md_table = process_temp_table(doc, temp_md_table)
152
-
153
- row = res[2]
154
-
155
- if temp_md_list
156
- temp_md_list.add_row(s)
157
- else
158
- item = MarkdownList.new(doc, false)
159
- item.add_row(s)
160
- temp_md_list = item
161
- end
162
-
163
- elsif res = /^\d[.]\s(.*)/.match(s) # check if ordered list start
164
-
165
- temp_md_table = process_temp_table(doc, temp_md_table)
166
-
167
- row = res[1]
168
-
169
- if temp_md_list
170
- temp_md_list.add_row(s)
171
- else
172
- item = MarkdownList.new(doc, true)
173
- item.add_row(s)
174
- temp_md_list = item
175
- end
176
-
177
- elsif s[0] == '|' # check if table
178
-
179
- if temp_md_list
180
- doc.items.append temp_md_list
181
- temp_md_list = nil
182
- end
183
-
184
- if res = /^[|](-{3,})[|]/.match(s) # check if it is a separator first
185
-
186
- if temp_md_table
187
- # separator is found after heading
188
- temp_md_table.is_separator_detected = true
189
- else
190
- # separator out of table scope consider it just as a regular paragraph
191
- item = Paragraph.new(doc, s)
192
- doc.items.append(item)
193
- end
194
-
195
- elsif res = /^[|](.*[|])/.match(s) # check if it looks as a table row
196
-
197
- row = res[1]
198
-
199
- if temp_md_table
200
- if temp_md_table.is_separator_detected # if there is a separator
201
- # check if parent doc is a Protocol
202
- if doc.instance_of? Protocol
203
- # check if it is a controlled table
204
- tmp = /(.*)\s+>\[(\S*)\]/.match(row)
205
- if tmp && (temp_md_table.instance_of? MarkdownTable)
206
- # this is not a regular Markdown table
207
- # so the table type shall be changed and this row shall be passed one more time
208
- temp_md_table = ControlledTable.new(doc, temp_md_table)
209
- end
210
- end
211
- temp_md_table.add_row(row)
212
- else
213
- # replece table heading with regular paragraph
214
- item = Paragraph.new(doc, temp_md_table.heading_row)
215
- doc.items.append(item)
216
- # and current row
217
- item = Paragraph.new(doc, s)
218
- doc.items.append(item)
219
- temp_md_table = nil
220
- end
221
- else
222
- # start table from heading
223
- temp_md_table = MarkdownTable.new(doc, s)
224
- end
225
- end
226
-
227
- elsif res = /^>(.*)/.match(s) # check if blockquote
228
-
229
- temp_md_table = process_temp_table(doc, temp_md_table)
230
-
231
- if temp_md_list
232
- doc.items.append temp_md_list
233
- temp_md_list = nil
234
- end
235
-
236
- item = Blockquote.new(res[1])
237
- item.parent_doc = doc
238
- item.parent_heading = doc.headings[-1]
239
- doc.items.append(item)
240
-
241
- elsif res = /^```(\w*)/.match(s) # check if code block
242
-
243
- temp_md_table = process_temp_table(doc, temp_md_table)
244
- if temp_md_list
245
- doc.items.append temp_md_list
246
- temp_md_list = nil
247
- end
248
-
249
- suggested_format = ''
250
- suggested_format = res[1] if res.length == 2
251
-
252
- if temp_code_block
253
- # close already opened block
254
- doc.items.append(temp_code_block)
255
- temp_code_block = nil
256
- else
257
- # start code block
258
- temp_code_block = CodeBlock.new(suggested_format)
259
- temp_code_block.parent_doc = doc
260
- end
261
-
262
- elsif res = /^TODO:(.*)/.match(s) # check if TODO block
263
-
264
- temp_md_table = process_temp_table(doc, temp_md_table)
265
- if temp_md_list
266
- doc.items.append temp_md_list
267
- temp_md_list = nil
268
- end
269
-
270
- text = '**TODO**: ' + res[1]
271
-
272
- item = TodoBlock.new(text)
273
- item.parent_doc = doc
274
- item.parent_heading = doc.headings[-1]
275
- doc.items.append(item)
276
- doc.todo_blocks.append(item)
277
-
278
- else # Reqular Paragraph
279
- temp_md_table = process_temp_table(doc, temp_md_table)
280
- if temp_md_list
281
- if MarkdownList.unordered_list_item?(s) || MarkdownList.ordered_list_item?(s)
282
- temp_md_list.add_row(s)
283
- next
284
- else
285
- doc.items.append temp_md_list
286
- temp_md_list = nil
287
- end
288
- end
289
- if temp_code_block
290
- temp_code_block.code_lines.append(s)
291
- else
292
- item = Paragraph.new(doc, s)
293
- doc.items.append(item)
294
- end
295
- end
296
- elsif temp_md_list
297
- doc.items.append temp_md_list
298
- temp_md_list = nil # lists are separated by emty line from each other
299
- end
300
- end
301
- # Finalize non-closed elements
302
- temp_md_table = process_temp_table(doc, temp_md_table)
303
- if temp_md_list
304
- doc.items.append temp_md_list
305
- temp_md_list = nil
306
- end
307
- if temp_code_block
308
- doc.items.append temp_code_block
309
- temp_code_block = nil
310
- end
311
- # Add footer to close opened tables if any
312
- item = DocFooter.new
313
- item.parent_doc = doc
314
- doc.items.append(item)
315
- end
316
-
317
- def self.process_temp_table(doc, temp_md_table) # rubocop:disable Metrics/MethodLength
318
- if temp_md_table
319
- if temp_md_table.is_separator_detected
320
- doc.items.append temp_md_table
321
- else # no separator
322
- # replece table heading with regular paragraph
323
- item = Paragraph.new(doc, temp_md_table.heading_row)
324
- doc.items.append(item)
325
- end
326
- temp_md_table = nil
327
- end
328
- temp_md_table
329
- end
330
- end
1
+ require_relative 'doc_items/text_line'
2
+ require_relative 'doc_items/doc_item'
3
+ require_relative 'doc_items/heading'
4
+ require_relative 'doc_items/paragraph'
5
+ require_relative 'doc_items/blockquote'
6
+ require_relative 'doc_items/code_block'
7
+ require_relative 'doc_items/todo_block'
8
+ require_relative 'doc_items/controlled_paragraph'
9
+ require_relative 'doc_items/markdown_table'
10
+ require_relative 'doc_items/controlled_table'
11
+ require_relative 'doc_items/image'
12
+ require_relative 'doc_items/markdown_list'
13
+ require_relative 'doc_items/doc_footer'
14
+ require_relative 'doc_items/frontmatter'
15
+
16
+ class DocParser # rubocop:disable Metrics/ClassLength,Style/Documentation
17
+ def self.try_to_extract_frontmatter(doc, text_lines) # rubocop:disable Metrics/MethodLength
18
+ lines_to_remove = 0
19
+ frontmatter_lines = ''
20
+ if /^(-{3,})/.match(text_lines[0])
21
+ frontmatter_started = false
22
+ text_lines.each do |s|
23
+ lines_to_remove += 1
24
+ if /^(-{3,})/.match(s)
25
+ if frontmatter_started
26
+ doc.frontmatter = Frontmatter.new(frontmatter_lines)
27
+ frontmatter_started = false
28
+ break
29
+ else
30
+ frontmatter_started = true
31
+ end
32
+ elsif frontmatter_started
33
+ frontmatter_lines += s
34
+ end
35
+ end
36
+ end
37
+ text_lines.shift(lines_to_remove)
38
+ text_lines
39
+ end
40
+
41
+ def self.parse(doc, text_lines)
42
+ temp_md_table = nil
43
+ temp_md_list = nil
44
+ temp_code_block = nil
45
+ # restart section numbering for each new document
46
+ Heading.reset_global_section_number
47
+ # try to get frontmatter first
48
+ text_lines = try_to_extract_frontmatter(doc, text_lines)
49
+ # There is no document without heading
50
+ title = "#{doc.id}.md"
51
+ item = Heading.new(doc, title, 0)
52
+ doc.items.append(item)
53
+ doc.headings.append(item)
54
+ doc.title = title
55
+ # replace dummy title with extracted from frontmatter
56
+ if doc.frontmatter && (doc.frontmatter.parameters.key? 'title')
57
+ doc.title = doc.frontmatter.parameters['title']
58
+ doc.headings[0].text = doc.frontmatter.parameters['title']
59
+ end
60
+ # main loop
61
+ text_lines.each do |s|
62
+ if s.lstrip != ''
63
+ if res = /^(\#{1,})\s(.*)/.match(s) # Heading
64
+
65
+ temp_md_table = process_temp_table(doc, temp_md_table)
66
+ if temp_md_list
67
+ doc.items.append temp_md_list
68
+ temp_md_list = nil
69
+ end
70
+
71
+ level = res[1].length
72
+ value = res[2]
73
+
74
+ item = Heading.new(doc, value, level)
75
+ doc.items.append(item)
76
+ doc.headings.append(item)
77
+
78
+ elsif res = /^%\s(.*)/.match(s) # Pandoc Document Title
79
+
80
+ title = res[1]
81
+
82
+ # Rewrite
83
+ doc.title = title
84
+ doc.headings[0].text = title
85
+
86
+ elsif res = /^\[(\S*)\]\s+(.*)/.match(s) # Controlled Paragraph
87
+
88
+ temp_md_table = process_temp_table(doc, temp_md_table)
89
+ if temp_md_list
90
+ doc.items.append temp_md_list
91
+ temp_md_list = nil
92
+ end
93
+
94
+ id = res[1].upcase
95
+ text = res[2]
96
+ up_links = nil
97
+
98
+ # check if it contains the uplink (one or many)
99
+ # TODO: check this regular expression
100
+ first_pos = text.length # for trailing commas
101
+ tmp = text.scan(/(>\[(?>[^\[\]]|\g<0>)*\])/) # >[SRS-001], >[SYS-002]
102
+ if tmp.length > 0
103
+ up_links = []
104
+ tmp.each do |ul|
105
+ lnk = ul[0]
106
+ # do not add links for the self document
107
+ doc_id = /([a-zA-Z]+)-\d+/.match(lnk) # SRS
108
+ up_links << lnk.upcase if doc_id and (doc_id[1].downcase != doc.id.downcase)
109
+ # try to find the real end of text
110
+ pos = text.index(lnk)
111
+ first_pos = pos if pos < first_pos
112
+ # remove uplink from text
113
+ text = text.split(lnk, 1).join('')
114
+ end
115
+ # remove trailing commas and spaces
116
+ if text.length > first_pos
117
+ first_pos -= 1
118
+ text = text[0..first_pos].strip
119
+ end
120
+ end
121
+
122
+ # since we already know id and text
123
+ item = ControlledParagraph.new(doc, text, id)
124
+
125
+ if up_links
126
+ up_links.uniq! # remove duplicates
127
+ doc.items_with_uplinks_number += 1 # for statistics
128
+ up_links.each do |ul|
129
+ next unless tmp = />\[(\S*)\]$/.match(ul) # >[SRS-001]
130
+
131
+ up_link_id = tmp[1]
132
+
133
+ item.up_link_ids = [] unless item.up_link_ids
134
+
135
+ item.up_link_ids.append(up_link_id)
136
+
137
+ if tmp = /^([a-zA-Z]+)-\d+/.match(up_link_id) # SRS
138
+ doc.up_link_docs[tmp[1].downcase.to_s] = tmp[1].downcase # multiple documents could be up-linked
139
+ end
140
+ end
141
+ end
142
+
143
+ doc.items.append(item)
144
+ # for statistics
145
+ if doc.dictionary.has_key?(id.to_s)
146
+ doc.duplicated_ids_number += 1
147
+ doc.duplicates_list.append(item)
148
+ else
149
+ doc.dictionary[id.to_s] = item # for fast search
150
+ end
151
+ doc.controlled_items.append(item) # for fast search
152
+
153
+ # for statistics
154
+ n = /\d+/.match(id)[0].to_i
155
+ if n > doc.last_used_id_number
156
+ doc.last_used_id = id
157
+ doc.last_used_id_number = n
158
+ end
159
+
160
+ elsif res = /^!\[(.*)\]\((.*)\)/.match(s) # Image
161
+
162
+ temp_md_table = process_temp_table(doc, temp_md_table)
163
+ if temp_md_list
164
+ doc.items.append temp_md_list
165
+ temp_md_list = nil
166
+ end
167
+
168
+ img_text = res[1]
169
+ img_path = res[2]
170
+
171
+ item = Image.new(img_text, img_path)
172
+ item.parent_doc = doc
173
+ item.parent_heading = doc.headings[-1]
174
+
175
+ doc.items.append(item)
176
+
177
+ elsif res = /^(\*\s+)(.*)/.match(s) # check if unordered list start
178
+
179
+ if doc.title == ''
180
+ # dummy section if root is not a Document Title (level 0)
181
+ title = "#{doc.id}.md"
182
+ item = Heading.new(doc, title, 0)
183
+ doc.items.append(item)
184
+ doc.headings.append(item)
185
+ doc.title = title
186
+ end
187
+
188
+ temp_md_table = process_temp_table(doc, temp_md_table)
189
+
190
+ row = res[2]
191
+
192
+ if temp_md_list
193
+ temp_md_list.add_row(s)
194
+ else
195
+ item = MarkdownList.new(doc, false)
196
+ item.add_row(s)
197
+ temp_md_list = item
198
+ end
199
+
200
+ elsif res = /^\d[.]\s(.*)/.match(s) # check if ordered list start
201
+
202
+ temp_md_table = process_temp_table(doc, temp_md_table)
203
+
204
+ row = res[1]
205
+
206
+ if temp_md_list
207
+ temp_md_list.add_row(s)
208
+ else
209
+ item = MarkdownList.new(doc, true)
210
+ item.add_row(s)
211
+ temp_md_list = item
212
+ end
213
+
214
+ elsif s[0] == '|' # check if table
215
+
216
+ if doc.title == ''
217
+ # dummy section if root is not a Document Title (level 0)
218
+ title = "#{doc.id}.md"
219
+ item = Heading.new(doc, title, 0)
220
+ doc.items.append(item)
221
+ doc.headings.append(item)
222
+ doc.title = title
223
+ end
224
+
225
+ if temp_md_list
226
+ doc.items.append temp_md_list
227
+ temp_md_list = nil
228
+ end
229
+
230
+ if res = /^[|](-{3,})[|]/.match(s) # check if it is a separator first
231
+
232
+ if temp_md_table
233
+ # separator is found after heading
234
+ temp_md_table.is_separator_detected = true
235
+ else
236
+ # separator out of table scope consider it just as a regular paragraph
237
+ item = Paragraph.new(doc, s)
238
+ doc.items.append(item)
239
+ end
240
+
241
+ elsif res = /^[|](.*[|])/.match(s) # check if it looks as a table row
242
+
243
+ row = res[1]
244
+
245
+ if temp_md_table
246
+ if temp_md_table.is_separator_detected # if there is a separator
247
+ # check if parent doc is a Protocol
248
+ if doc.instance_of? Protocol
249
+ # check if it is a controlled table
250
+ tmp = /(.*)\s+>\[(\S*)\]/.match(row)
251
+ if tmp && (temp_md_table.instance_of? MarkdownTable)
252
+ # this is not a regular Markdown table
253
+ # so the table type shall be changed and this row shall be passed one more time
254
+ temp_md_table = ControlledTable.new(doc, temp_md_table)
255
+ end
256
+ end
257
+ temp_md_table.add_row(row)
258
+ else
259
+ # replece table heading with regular paragraph
260
+ item = Paragraph.new(doc, temp_md_table.heading_row)
261
+ doc.items.append(item)
262
+ # and current row
263
+ item = Paragraph.new(doc, s)
264
+ doc.items.append(item)
265
+ temp_md_table = nil
266
+ end
267
+ else
268
+ # start table from heading
269
+ temp_md_table = MarkdownTable.new(doc, s)
270
+ end
271
+ end
272
+
273
+ elsif res = /^>(.*)/.match(s) # check if blockquote
274
+
275
+ temp_md_table = process_temp_table(doc, temp_md_table)
276
+
277
+ if temp_md_list
278
+ doc.items.append temp_md_list
279
+ temp_md_list = nil
280
+ end
281
+
282
+ item = Blockquote.new(res[1])
283
+ item.parent_doc = doc
284
+ item.parent_heading = doc.headings[-1]
285
+ doc.items.append(item)
286
+
287
+ elsif res = /^```(\w*)/.match(s) # check if code block
288
+
289
+ temp_md_table = process_temp_table(doc, temp_md_table)
290
+ if temp_md_list
291
+ doc.items.append temp_md_list
292
+ temp_md_list = nil
293
+ end
294
+
295
+ suggested_format = ''
296
+ suggested_format = res[1] if res.length == 2
297
+
298
+ if temp_code_block
299
+ # close already opened block
300
+ doc.items.append(temp_code_block)
301
+ temp_code_block = nil
302
+ else
303
+ # start code block
304
+ temp_code_block = CodeBlock.new(suggested_format)
305
+ temp_code_block.parent_doc = doc
306
+ temp_code_block.parent_heading = doc.headings[-1]
307
+ end
308
+
309
+ elsif res = /^TODO:(.*)/.match(s) # check if TODO block
310
+
311
+ temp_md_table = process_temp_table(doc, temp_md_table)
312
+ if temp_md_list
313
+ doc.items.append temp_md_list
314
+ temp_md_list = nil
315
+ end
316
+
317
+ text = '**TODO**: ' + res[1]
318
+
319
+ item = TodoBlock.new(text)
320
+ item.parent_doc = doc
321
+ item.parent_heading = doc.headings[-1]
322
+ doc.items.append(item)
323
+ doc.todo_blocks.append(item)
324
+
325
+ else # Reqular Paragraph
326
+
327
+ temp_md_table = process_temp_table(doc, temp_md_table)
328
+ if temp_md_list
329
+ if MarkdownList.unordered_list_item?(s) || MarkdownList.ordered_list_item?(s)
330
+ temp_md_list.add_row(s)
331
+ next
332
+ else
333
+ doc.items.append temp_md_list
334
+ temp_md_list = nil
335
+ end
336
+ end
337
+ if temp_code_block
338
+ temp_code_block.code_lines.append(s)
339
+ else
340
+ item = Paragraph.new(doc, s)
341
+ doc.items.append(item)
342
+ end
343
+ end
344
+ elsif temp_md_list
345
+ doc.items.append temp_md_list
346
+ temp_md_list = nil # lists are separated by emty line from each other
347
+ end
348
+ end
349
+ # Finalize non-closed elements
350
+ temp_md_table = process_temp_table(doc, temp_md_table)
351
+ if temp_md_list
352
+ doc.items.append temp_md_list
353
+ temp_md_list = nil
354
+ end
355
+ if temp_code_block
356
+ doc.items.append temp_code_block
357
+ temp_code_block = nil
358
+ end
359
+ # Add footer to close opened tables if any
360
+ item = DocFooter.new
361
+ item.parent_doc = doc
362
+ doc.items.append(item)
363
+ end
364
+
365
+ def self.process_temp_table(doc, temp_md_table)
366
+ if temp_md_table
367
+ if temp_md_table.is_separator_detected
368
+ doc.items.append temp_md_table
369
+ else # no separator
370
+ # replece table heading with regular paragraph
371
+ item = Paragraph.new(doc, temp_md_table.heading_row)
372
+ doc.items.append(item)
373
+ end
374
+ temp_md_table = nil
375
+ end
376
+ temp_md_table
377
+ end
378
+ end