asciidoctor 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,17 @@
1
1
  module Asciidoctor
2
2
  def self.debug(*args)
3
- puts *args unless ENV['SUPPRESS_DEBUG']
3
+ puts *args if self.show_debug_output?
4
+ end
5
+
6
+ def self.show_debug_output?
7
+ ENV['DEBUG'] == 'true' && ENV['SUPPRESS_DEBUG'] != 'true'
8
+ end
9
+
10
+ def self.puts_indented(level, *args)
11
+ thing = " "*level*2
12
+ args.each do |arg|
13
+ self.debug "#{thing}#{arg}"
14
+ end
4
15
  end
5
16
  end
6
17
 
@@ -4,13 +4,6 @@ class Asciidoctor::Document
4
4
 
5
5
  include Asciidoctor
6
6
 
7
- # Public: Get the String document source.
8
- attr_reader :source
9
-
10
- # Public: Get the Asciidoctor::Renderer instance currently being used
11
- # to render this Document.
12
- attr_reader :renderer
13
-
14
7
  # Public: Get the Hash of defines
15
8
  attr_reader :defines
16
9
 
@@ -25,111 +18,32 @@ class Asciidoctor::Document
25
18
 
26
19
  # Public: Initialize an Asciidoc object.
27
20
  #
28
- # data - The String Asciidoc source document.
21
+ # data - The Array of Strings holding the Asciidoc source document.
29
22
  # block - A block that can be used to retrieve external Asciidoc
30
23
  # data to include in this document.
31
24
  #
32
25
  # Examples
33
26
  #
34
- # base = File.dirname(filename)
35
- # data = File.read(filename)
27
+ # data = File.readlines(filename)
36
28
  # doc = Asciidoctor::Document.new(data)
37
- def initialize(data, &block)
38
- raw_source = []
29
+ def initialize(data, options = {}, &block)
39
30
  @elements = []
40
- @defines = {}
41
- @references = {}
31
+ @options = options
42
32
 
43
- include_regexp = /^include::([^\[]+)\[\]\s*\n?\z/
44
- data.each do |line|
45
- if inc = line.match(include_regexp)
46
- raw_source.concat(File.readlines(inc[1]))
47
- else
48
- raw_source << line
49
- end
50
- end
33
+ reader = Reader.new(data, &block)
51
34
 
52
- ifdef_regexp = /^(ifdef|ifndef)::([^\[]+)\[\]/
53
- endif_regexp = /^endif::/
54
- defattr_regexp = /^:([^:]+):\s*(.*)\s*$/
55
- conditional_regexp = /^\s*\{([^\?]+)\?\s*([^\}]+)\s*\}/
56
-
57
- skip_to = nil
58
- continuing_value = nil
59
- continuing_key = nil
60
- @lines = []
61
- raw_source.each do |line|
62
- if skip_to
63
- skip_to = nil if line.match(skip_to)
64
- elsif continuing_value
65
- close_continue = false
66
- # Lines that start with whitespace and end with a '+' are
67
- # a continuation, so gobble them up into `value`
68
- if match = line.match(/\s+(.+)\s+\+\s*$/)
69
- continuing_value += match[1]
70
- elsif match = line.match(/\s+(.+)/)
71
- # If this continued line doesn't end with a +, then this
72
- # is the end of the continuation, no matter what the next
73
- # line does.
74
- continuing_value += match[1]
75
- close_continue = true
76
- else
77
- # If this line doesn't start with whitespace, then it's
78
- # not a valid continuation line, so push it back for processing
79
- close_continue = true
80
- raw_source.unshift(line)
81
- end
82
- if close_continue
83
- @defines[continuing_key] = continuing_value
84
- continuing_key = nil
85
- continuing_value = nil
86
- end
87
- elsif match = line.match(ifdef_regexp)
88
- attr = match[2]
89
- skip = case match[1]
90
- when 'ifdef'; !@defines.has_key?(attr)
91
- when 'ifndef'; @defines.has_key?(attr)
92
- end
93
- skip_to = /^endif::#{attr}\[\]\s*\n/ if skip
94
- elsif match = line.match(defattr_regexp)
95
- key = match[1]
96
- value = match[2]
97
- if match = value.match(/(.+)\s+\+\s*$/)
98
- # continuation line, grab lines until we run out of continuation lines
99
- continuing_key = key
100
- continuing_value = match[1] # strip off the spaces and +
101
- Asciidoctor.debug "continuing key: #{continuing_key} with partial value: '#{continuing_value}'"
102
- else
103
- @defines[key] = value
104
- Asciidoctor.debug "Defines[#{key}] is '#{value}'"
105
- end
106
- elsif !line.match(endif_regexp)
107
- while match = line.match(conditional_regexp)
108
- value = @defines.has_key?(match[1]) ? match[2] : ''
109
- line.sub!(conditional_regexp, value)
110
- end
111
- @lines << line unless line.match(REGEXP[:comment])
112
- end
113
- end
114
-
115
- # Process bibliography references, so they're available when text
116
- # before the reference is being rendered.
117
- @lines.each do |line|
118
- if biblio = line.match(REGEXP[:biblio])
119
- references[biblio[1]] = "[#{biblio[1]}]"
120
- end
121
- end
122
-
123
- @source = @lines.join
35
+ # pseudo-delegation :)
36
+ @defines = reader.defines
37
+ @references = reader.references
124
38
 
125
39
  # Now parse @lines into elements
126
- while @lines.any?
127
- skip_blank(@lines)
40
+ while reader.has_lines?
41
+ reader.skip_blank
128
42
 
129
- @elements << next_block(@lines) if @lines.any?
43
+ @elements << Lexer.next_block(reader, self) if reader.has_lines?
130
44
  end
131
45
 
132
- Asciidoctor.debug "Found #{@elements.size} elements:"
46
+ Asciidoctor.debug "Found #{@elements.size} elements in this document:"
133
47
  @elements.each do |el|
134
48
  Asciidoctor.debug el
135
49
  end
@@ -161,24 +75,35 @@ class Asciidoctor::Document
161
75
 
162
76
  def splain
163
77
  if @header
164
- puts "Header is #{@header}"
78
+ Asciidoctor.debug "Header is #{@header}"
165
79
  else
166
- puts "No header"
80
+ Asciidoctor.debug "No header"
167
81
  end
168
82
 
169
- puts "I have #{@elements.count} elements"
83
+ Asciidoctor.debug "I have #{@elements.count} elements"
170
84
  @elements.each_with_index do |block, i|
171
- puts "Block ##{i} is a #{block.class}"
172
- puts "Name is #{block.name rescue 'n/a'}"
173
- puts "=" * 40
85
+ Asciidoctor.debug "v" * 60
86
+ Asciidoctor.debug "Block ##{i} is a #{block.class}"
87
+ Asciidoctor.debug "Name is #{block.name rescue 'n/a'}"
88
+ block.splain(0) if block.respond_to? :splain
89
+ Asciidoctor.debug "^" * 60
90
+ end
91
+ nil
92
+ end
93
+
94
+ def renderer
95
+ return @renderer if @renderer
96
+ render_options = {}
97
+ if @options[:template_dir]
98
+ render_options[:template_dir] = @options[:template_dir]
174
99
  end
100
+ @renderer = Renderer.new(render_options)
175
101
  end
176
102
 
177
103
  # Public: Render the Asciidoc document using erb templates
178
104
  #
179
105
  def render
180
- @renderer ||= Renderer.new
181
- html = self.renderer.render('document', self, :header => @header, :preamble => @preamble)
106
+ html = renderer.render('document', self, :header => @header, :preamble => @preamble)
182
107
  end
183
108
 
184
109
  def content
@@ -190,528 +115,4 @@ class Asciidoctor::Document
190
115
  html_pieces.join("\n")
191
116
  end
192
117
 
193
- private
194
-
195
- # Private: Strip off leading blank lines in the Array of lines.
196
- #
197
- # lines - the Array of String lines.
198
- #
199
- # Returns nil.
200
- #
201
- # Examples
202
- #
203
- # content
204
- # => ["\n", "\t\n", "Foo\n", "Bar\n", "\n"]
205
- #
206
- # skip_blank(content)
207
- # => nil
208
- #
209
- # lines
210
- # => ["Foo\n", "Bar\n"]
211
- def skip_blank(lines)
212
- while lines.any? && lines.first.strip.empty?
213
- lines.shift
214
- end
215
-
216
- nil
217
- end
218
-
219
- # Private: Strip off and return the list item segment (one or more contiguous blocks) from the Array of lines.
220
- #
221
- # lines - the Array of String lines.
222
- # options - an optional Hash of processing options:
223
- # * :alt_ending may be used to specify a regular expression match other than
224
- # a blank line to signify the end of the segment.
225
- # Returns the Array of lines from the next segment.
226
- #
227
- # Examples
228
- #
229
- # content
230
- # => ["First paragraph\n", "+\n", "Second paragraph\n", "--\n", "Open block\n", "\n", "Can have blank lines\n", "--\n", "\n", "In a different segment\n"]
231
- #
232
- # list_item_segment(content)
233
- # => ["First paragraph\n", "+\n", "Second paragraph\n", "--\n", "Open block\n", "\n", "Can have blank lines\n", "--\n"]
234
- #
235
- # content
236
- # => ["In a different segment\n"]
237
- def list_item_segment(lines, options={})
238
- alternate_ending = options[:alt_ending]
239
- segment = []
240
-
241
- skip_blank(lines)
242
-
243
- # Grab lines until the first blank line not inside an open block
244
- # or listing
245
- in_oblock = false
246
- in_listing = false
247
- while lines.any?
248
- this_line = lines.shift
249
- in_oblock = !in_oblock if this_line.match(REGEXP[:oblock])
250
- in_listing = !in_listing if this_line.match(REGEXP[:listing])
251
- if !in_oblock && !in_listing
252
- if this_line.strip.empty?
253
- # From the Asciidoc user's guide:
254
- # Another list or a literal paragraph immediately following
255
- # a list item will be implicitly included in the list item
256
- next_nonblank = lines.detect{|l| !l.strip.empty?}
257
- if !next_nonblank.nil? &&
258
- ( alternate_ending.nil? ||
259
- !next_nonblank.match(alternate_ending)
260
- ) && [:ulist, :olist, :colist, :dlist, :lit_par, :continue].
261
- find { |pattern| next_nonblank.match(REGEXP[pattern]) }
262
-
263
- # Pull blank lines into the segment, so the next thing up for processing
264
- # will be the next nonblank line.
265
- while lines.first.strip.empty?
266
- segment << this_line
267
- this_line = lines.shift
268
- end
269
- else
270
- break
271
- end
272
- elsif !alternate_ending.nil? && this_line.match(alternate_ending)
273
- lines.unshift this_line
274
- break
275
- end
276
- end
277
-
278
- segment << this_line
279
- end
280
-
281
- segment
282
- end
283
-
284
- # Private: Return all the lines from `lines` until we run out of lines,
285
- # find a blank line with :break_on_blank_lines => true, or find a line
286
- # for which the given block evals to true.
287
- #
288
- # lines - the Array of String lines.
289
- # options - an optional Hash of processing options:
290
- # * :break_on_blank_lines may be used to specify to break on blank lines
291
- # * :preserve_last_line may be used to specify that the String
292
- # causing the method to stop processing lines should be
293
- # pushed back onto the `lines` Array.
294
- #
295
- # Returns the Array of lines from the next segment.
296
- #
297
- # Examples
298
- #
299
- # content
300
- # => ["First paragraph\n", "Second paragraph\n", "Open block\n", "\n", "Can have blank lines\n", "--\n", "\n", "In a different segment\n"]
301
- #
302
- # grab_lines_until(content)
303
- # => ["First paragraph\n", "Second paragraph\n", "Open block\n"]
304
- #
305
- # content
306
- # => ["In a different segment\n"]
307
- def grab_lines_until(lines, options = {}, &block)
308
- buffer = []
309
-
310
- while (this_line = lines.shift)
311
- Asciidoctor.debug "Processing line: '#{this_line}'"
312
- finis = this_line.nil?
313
- finis ||= true if options[:break_on_blank_lines] && this_line.strip.empty?
314
- finis ||= true if block && value = yield(this_line)
315
- if finis
316
- lines.unshift(this_line) if options[:preserve_last_line] and ! this_line.nil?
317
- break
318
- end
319
-
320
- buffer << this_line
321
- end
322
- buffer
323
- end
324
-
325
- # Private: Return the next block from the section.
326
- #
327
- # * Skip over blank lines to find the start of the next content block.
328
- # * Use defined regular expressions to determine the type of content block.
329
- # * Based on the type of content block, grab lines to the end of the block.
330
- # * Return a new Asciidoctor::Block or Asciidoctor::Section instance with the
331
- # content set to the grabbed lines.
332
- def next_block(lines, parent = self)
333
- # Skip ahead to the block content
334
- skip_blank(lines)
335
-
336
- return nil if lines.empty?
337
-
338
- # NOTE: An anchor looks like this:
339
- # [[foo]]
340
- # with the inside [foo] (including brackets) as match[1]
341
- if match = lines.first.match(REGEXP[:anchor])
342
- Asciidoctor.debug "Found an anchor in line:\n\t#{lines.first}"
343
- # NOTE: This expression conditionally strips off the brackets from
344
- # [foo], though REGEXP[:anchor] won't actually match without
345
- # match[1] being bracketed, so the condition isn't necessary.
346
- anchor = match[1].match(/^\[(.*)\]/) ? $1 : match[1]
347
- # NOTE: Set @references['foo'] = '[foo]'
348
- @references[anchor] = match[1]
349
- lines.shift
350
- else
351
- anchor = nil
352
- end
353
-
354
- Asciidoctor.debug "/"*64
355
- Asciidoctor.debug "#{__FILE__}:#{__LINE__} - First two lines are:"
356
- Asciidoctor.debug lines.first
357
- Asciidoctor.debug lines[1]
358
- Asciidoctor.debug "/"*64
359
-
360
- block = nil
361
- title = nil
362
- caption = nil
363
- source_type = nil
364
- buffer = []
365
- while lines.any? && block.nil?
366
- buffer.clear
367
- this_line = lines.shift
368
- next_line = lines.first || ''
369
-
370
- if this_line.match(REGEXP[:comment])
371
- next
372
-
373
- elsif match = this_line.match(REGEXP[:title])
374
- title = match[1]
375
- skip_blank(lines)
376
-
377
- elsif match = this_line.match(REGEXP[:listing_source])
378
- source_type = match[1]
379
- skip_blank(lines)
380
-
381
- elsif match = this_line.match(REGEXP[:caption])
382
- caption = match[1]
383
-
384
- elsif is_section_heading?(this_line, next_line)
385
- # If we've come to a new section, then we've found the end of this
386
- # current block. Likewise if we'd found an unassigned anchor, push
387
- # it back as well, so it can go with this next heading.
388
- # NOTE - I don't think this will assign the anchor properly. Anchors
389
- # only match with double brackets - [[foo]], but what's stored in
390
- # `anchor` at this point is only the `foo` part that was stripped out
391
- # after matching. TODO: Need a way to test this.
392
- lines.unshift(this_line)
393
- lines.unshift(anchor) unless anchor.nil?
394
- Asciidoctor.debug "SENDING to next_section with lines[0] = #{lines.first}"
395
- block = next_section(lines)
396
-
397
- elsif this_line.match(REGEXP[:oblock])
398
- # oblock is surrounded by '--' lines and has zero or more blocks inside
399
- buffer = grab_lines_until(lines) { |line| line.match(REGEXP[:oblock]) }
400
-
401
- while buffer.any? && buffer.last.strip.empty?
402
- buffer.pop
403
- end
404
-
405
- block = Block.new(parent, :oblock, [])
406
- while buffer.any?
407
- block.blocks << next_block(buffer, block)
408
- end
409
-
410
- elsif list_type = [:olist, :ulist, :colist].detect{|l| this_line.match( REGEXP[l] )}
411
- items = []
412
- block = Block.new(parent, list_type)
413
- while !this_line.nil? && match = this_line.match(REGEXP[list_type])
414
- item = ListItem.new
415
-
416
- lines.unshift match[2].lstrip.sub(/^\./, '\.')
417
- item_segment = list_item_segment(lines, :alt_ending => REGEXP[list_type])
418
- while item_segment.any?
419
- item.blocks << next_block(item_segment, block)
420
- end
421
-
422
- if item.blocks.any? &&
423
- item.blocks.first.is_a?(Block) &&
424
- (item.blocks.first.context == :paragraph || item.blocks.first.context == :literal)
425
- item.content = item.blocks.shift.buffer.map{|l| l.strip}.join("\n")
426
- end
427
-
428
- items << item
429
-
430
- skip_blank(lines)
431
-
432
- this_line = lines.shift
433
- end
434
- lines.unshift(this_line) unless this_line.nil?
435
-
436
- block.buffer = items
437
-
438
- elsif match = this_line.match(REGEXP[:dlist])
439
- pairs = []
440
- block = Block.new(parent, :dlist)
441
-
442
- this_dlist = Regexp.new(/^#{match[1]}(.*)#{match[3]}\s*$/)
443
-
444
- while !this_line.nil? && match = this_line.match(this_dlist)
445
- if anchor = match[1].match( /\[\[([^\]]+)\]\]/ )
446
- dt = ListItem.new( $` + $' )
447
- dt.anchor = anchor[1]
448
- else
449
- dt = ListItem.new( match[1] )
450
- end
451
- dd = ListItem.new
452
- lines.shift if lines.any? && lines.first.strip.empty? # workaround eg. git-config OPTIONS --get-colorbool
453
-
454
- dd_segment = list_item_segment(lines, :alt_ending => this_dlist)
455
- while dd_segment.any?
456
- dd.blocks << next_block(dd_segment, block)
457
- end
458
-
459
- if dd.blocks.any? &&
460
- dd.blocks.first.is_a?(Block) &&
461
- (dd.blocks.first.context == :paragraph || dd.blocks.first.context == :literal)
462
- dd.content = dd.blocks.shift.buffer.map{|l| l.strip}.join("\n")
463
- end
464
-
465
- pairs << [dt, dd]
466
-
467
- skip_blank(lines)
468
-
469
- this_line = lines.shift
470
- end
471
- lines.unshift(this_line) unless this_line.nil?
472
- block.buffer = pairs
473
-
474
- elsif this_line.match(REGEXP[:verse])
475
- # verse is preceded by [verse] and lasts until a blank line
476
- buffer = grab_lines_until(lines, :break_on_blank_lines => true)
477
- block = Block.new(parent, :verse, buffer)
478
-
479
- elsif this_line.match(REGEXP[:note])
480
- # note is an admonition preceded by [NOTE] and lasts until a blank line
481
- buffer = grab_lines_until(lines, :break_on_blank_lines => true) {|line| line.match( REGEXP[:continue] ) }
482
- block = Block.new(parent, :note, buffer)
483
-
484
- elsif block_type = [:listing, :example].detect{|t| this_line.match( REGEXP[t] )}
485
- buffer = grab_lines_until(lines) {|line| line.match( REGEXP[block_type] )}
486
- block = Block.new(parent, block_type, buffer)
487
-
488
- elsif this_line.match( REGEXP[:quote] )
489
- block = Block.new(parent, :quote)
490
- buffer = grab_lines_until(lines) {|line| line.match( REGEXP[:quote] ) }
491
-
492
- while buffer.any?
493
- block.blocks << next_block(buffer, block)
494
- end
495
-
496
- elsif this_line.match(REGEXP[:lit_blk])
497
- # example is surrounded by '....' (4 or more '.' chars) lines
498
- buffer = grab_lines_until(lines) {|line| line.match( REGEXP[:lit_blk] ) }
499
- block = Block.new(parent, :literal, buffer)
500
-
501
- elsif this_line.match(REGEXP[:lit_par])
502
- # literal paragraph is contiguous lines starting with
503
- # one or more space or tab characters
504
-
505
- # So we need to actually include this one in the grab_lines group
506
- lines.unshift( this_line )
507
- buffer = grab_lines_until(lines, :preserve_last_line => true) {|line| ! line.match( REGEXP[:lit_par] ) }
508
-
509
- block = Block.new(parent, :literal, buffer)
510
-
511
- elsif this_line.match(REGEXP[:sidebar_blk])
512
- # example is surrounded by '****' (4 or more '*' chars) lines
513
- buffer = grab_lines_until(lines) {|line| line.match( REGEXP[:sidebar_blk] ) }
514
- block = Block.new(parent, :sidebar, buffer)
515
-
516
- else
517
- # paragraph is contiguous nonblank/noncontinuation lines
518
- while !this_line.nil? && !this_line.strip.empty?
519
- break if this_line.match(REGEXP[:continue])
520
- if this_line.match( REGEXP[:listing] ) || this_line.match( REGEXP[:oblock] )
521
- lines.unshift this_line
522
- break
523
- end
524
- buffer << this_line
525
- this_line = lines.shift
526
- end
527
-
528
- if buffer.any? && admonition = buffer.first.match(/^NOTE:\s*/)
529
- buffer[0] = admonition.post_match
530
- block = Block.new(parent, :note, buffer)
531
- elsif source_type
532
- block = Block.new(parent, :listing, buffer)
533
- else
534
- block = Block.new(parent, :paragraph, buffer)
535
- end
536
- end
537
- end
538
-
539
- block.anchor ||= anchor
540
- block.title ||= title
541
- block.caption ||= caption
542
-
543
- block
544
- end
545
-
546
- # Private: Get the Integer section level based on the characters
547
- # used in the ASCII line under the section name.
548
- #
549
- # line - the String line from under the section name.
550
- def section_level(line)
551
- char = line.strip.chars.to_a.uniq
552
- case char
553
- when ['=']; 0
554
- when ['-']; 1
555
- when ['~']; 2
556
- when ['^']; 3
557
- when ['+']; 4
558
- end
559
- end
560
-
561
- # == is level 0, === is level 1, etc.
562
- def single_line_section_level(line)
563
- [line.length - 1, 0].max
564
- end
565
-
566
- def is_single_line_section_heading?(line)
567
- !line.nil? && line.match(REGEXP[:level_title])
568
- end
569
-
570
- def is_two_line_section_heading?(line1, line2)
571
- !line1.nil? && !line2.nil? &&
572
- line1.match(REGEXP[:name]) && line2.match(REGEXP[:line]) &&
573
- (line1.size - line2.size).abs <= 1
574
- end
575
-
576
- def is_section_heading?(line1, line2 = nil)
577
- is_single_line_section_heading?(line1) ||
578
- is_two_line_section_heading?(line1, line2)
579
- end
580
-
581
- # Private: Extracts the name, level and (optional) embedded anchor from a
582
- # 1- or 2-line section heading.
583
- #
584
- # Returns an array of a String, Integer, and String or nil.
585
- #
586
- # Examples
587
- #
588
- # line1
589
- # => "Foo\n"
590
- # line2
591
- # => "~~~\n"
592
- #
593
- # name, level, anchor = extract_section_heading(line1, line2)
594
- #
595
- # name
596
- # => "Foo"
597
- # level
598
- # => 2
599
- # anchor
600
- # => nil
601
- #
602
- # line1
603
- # => "==== Foo\n"
604
- #
605
- # name, level, anchor = extract_section_heading(line1)
606
- #
607
- # name
608
- # => "Foo"
609
- # level
610
- # => 3
611
- # anchor
612
- # => nil
613
- #
614
- def extract_section_heading(line1, line2 = nil)
615
- Asciidoctor.debug "Processing line1: #{line1.chomp rescue 'nil'}, line2: #{line2.chomp rescue 'nil'}"
616
- sect_name = sect_anchor = nil
617
- sect_level = 0
618
-
619
- if is_single_line_section_heading?(line1)
620
- header_match = line1.match(REGEXP[:level_title])
621
- sect_name = header_match[2]
622
- sect_level = single_line_section_level(header_match[1])
623
- elsif is_two_line_section_heading?(line1, line2)
624
- header_match = line1.match(REGEXP[:name])
625
- if anchor_match = header_match[1].match(REGEXP[:anchor_embedded])
626
- sect_name = anchor_match[1]
627
- sect_anchor = anchor_match[2]
628
- else
629
- sect_name = header_match[1]
630
- end
631
- sect_level = section_level(line2)
632
- end
633
- Asciidoctor.debug "Returning #{sect_name}, #{sect_level}, and #{sect_anchor}"
634
- return [sect_name, sect_level, sect_anchor]
635
- end
636
-
637
- # Private: Return the next section from the document.
638
- #
639
- # Examples
640
- #
641
- # source
642
- # => "GREETINGS\n---------\nThis is my doc.\n\nSALUTATIONS\n-----------\nIt is awesome."
643
- #
644
- # doc = Asciidoctor::Document.new(source)
645
- #
646
- # doc.next_section
647
- # ["GREETINGS", [:paragraph, "This is my doc."]]
648
- #
649
- # doc.next_section
650
- # ["SALUTATIONS", [:paragraph, "It is awesome."]]
651
- def next_section(lines)
652
- section = Section.new(self)
653
-
654
- Asciidoctor.debug "%"*64
655
- Asciidoctor.debug "#{__FILE__}:#{__LINE__} - First two lines are:"
656
- Asciidoctor.debug lines.first
657
- Asciidoctor.debug lines[1]
658
- Asciidoctor.debug "%"*64
659
-
660
- # Skip ahead to the next section definition
661
- while lines.any? && section.name.nil?
662
- this_line = lines.shift
663
- next_line = lines.first || ''
664
- if match = this_line.match(REGEXP[:anchor])
665
- section.anchor = match[1]
666
- elsif is_section_heading?(this_line, next_line)
667
- section.name, section.level, section.anchor = extract_section_heading(this_line, next_line)
668
- lines.shift unless is_single_line_section_heading?(this_line)
669
- end
670
- end
671
-
672
- if !section.anchor.nil?
673
- anchor_id = section.anchor.match(/^\[(.*)\]/) ? $1 : section.anchor
674
- @references[anchor_id] = section.anchor
675
- section.anchor = anchor_id
676
- end
677
-
678
- # Grab all the lines that belong to this section
679
- section_lines = []
680
- while lines.any?
681
- this_line = lines.shift
682
- next_line = lines.first
683
-
684
- if is_section_heading?(this_line, next_line)
685
- _, this_level, _ = extract_section_heading(this_line, next_line)
686
- # A section can't contain a section level lower than itself,
687
- # so this signifies the end of the section.
688
- if this_level <= section.level
689
- lines.unshift this_line
690
- lines.unshift section_lines.pop if section_lines.any? && section_lines.last.match(REGEXP[:anchor])
691
- break
692
- else
693
- section_lines << this_line
694
- section_lines << lines.shift unless is_single_line_section_heading?(this_line)
695
- end
696
- elsif this_line.match(REGEXP[:listing])
697
- section_lines << this_line
698
- section_lines.concat grab_lines_until(lines) {|line| line.match( REGEXP[:listing] ) }
699
- # Also grab the last line, if there is one
700
- this_line = lines.shift
701
- section_lines << this_line unless this_line.nil?
702
- else
703
- section_lines << this_line
704
- end
705
- end
706
-
707
- # Now parse section_lines into Blocks
708
- while section_lines.any?
709
- skip_blank(section_lines)
710
-
711
- section << next_block(section_lines, section) if section_lines.any?
712
- end
713
-
714
- section
715
- end
716
- # end private
717
118
  end