asciidoctor 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of asciidoctor might be problematic. Click here for more details.

@@ -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