asciidoctor 1.5.7.1 → 1.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +95 -5
  3. data/Gemfile +23 -13
  4. data/README-de.adoc +482 -0
  5. data/README-fr.adoc +128 -119
  6. data/README-jp.adoc +2 -3
  7. data/README-zh_CN.adoc +2 -3
  8. data/README.adoc +131 -106
  9. data/asciidoctor.gemspec +9 -7
  10. data/data/locale/attributes-ar.adoc +1 -1
  11. data/data/locale/attributes-bg.adoc +1 -1
  12. data/data/locale/attributes-ca.adoc +1 -1
  13. data/data/locale/attributes-cs.adoc +1 -1
  14. data/data/locale/attributes-da.adoc +1 -1
  15. data/data/locale/attributes-de.adoc +1 -1
  16. data/data/locale/attributes-en.adoc +1 -1
  17. data/data/locale/attributes-es.adoc +1 -1
  18. data/data/locale/attributes-fa.adoc +1 -1
  19. data/data/locale/attributes-fi.adoc +1 -1
  20. data/data/locale/attributes-fr.adoc +1 -1
  21. data/data/locale/attributes-hu.adoc +1 -1
  22. data/data/locale/attributes-id.adoc +1 -1
  23. data/data/locale/attributes-it.adoc +1 -1
  24. data/data/locale/attributes-ja.adoc +1 -1
  25. data/data/locale/attributes-kr.adoc +1 -1
  26. data/data/locale/attributes-nb.adoc +1 -1
  27. data/data/locale/attributes-nl.adoc +1 -1
  28. data/data/locale/attributes-nn.adoc +1 -1
  29. data/data/locale/attributes-pl.adoc +1 -1
  30. data/data/locale/attributes-pt.adoc +1 -1
  31. data/data/locale/attributes-pt_BR.adoc +1 -1
  32. data/data/locale/attributes-ro.adoc +1 -1
  33. data/data/locale/attributes-ru.adoc +1 -1
  34. data/data/locale/attributes-sr.adoc +5 -4
  35. data/data/locale/attributes-sr_Latn.adoc +5 -4
  36. data/data/locale/attributes-sv.adoc +23 -0
  37. data/data/locale/attributes-tr.adoc +1 -1
  38. data/data/locale/attributes-uk.adoc +1 -1
  39. data/data/locale/attributes-zh_CN.adoc +1 -1
  40. data/data/locale/attributes-zh_TW.adoc +1 -1
  41. data/data/stylesheets/asciidoctor-default.css +23 -23
  42. data/lib/asciidoctor.rb +110 -104
  43. data/lib/asciidoctor/abstract_block.rb +55 -32
  44. data/lib/asciidoctor/abstract_node.rb +32 -17
  45. data/lib/asciidoctor/attribute_list.rb +8 -7
  46. data/lib/asciidoctor/block.rb +5 -7
  47. data/lib/asciidoctor/cli/options.rb +5 -9
  48. data/lib/asciidoctor/converter.rb +2 -2
  49. data/lib/asciidoctor/converter/docbook45.rb +7 -20
  50. data/lib/asciidoctor/converter/docbook5.rb +36 -37
  51. data/lib/asciidoctor/converter/factory.rb +10 -8
  52. data/lib/asciidoctor/converter/html5.rb +90 -65
  53. data/lib/asciidoctor/converter/manpage.rb +72 -62
  54. data/lib/asciidoctor/converter/template.rb +8 -6
  55. data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +5 -0
  56. data/lib/asciidoctor/document.rb +62 -10
  57. data/lib/asciidoctor/extensions.rb +74 -16
  58. data/lib/asciidoctor/helpers.rb +11 -14
  59. data/lib/asciidoctor/list.rb +2 -2
  60. data/lib/asciidoctor/parser.rb +223 -195
  61. data/lib/asciidoctor/path_resolver.rb +15 -7
  62. data/lib/asciidoctor/reader.rb +65 -36
  63. data/lib/asciidoctor/section.rb +6 -4
  64. data/lib/asciidoctor/substitutors.rb +170 -149
  65. data/lib/asciidoctor/table.rb +16 -8
  66. data/lib/asciidoctor/version.rb +1 -1
  67. data/man/asciidoctor.1 +6 -5
  68. data/man/asciidoctor.adoc +3 -2
  69. data/test/api_test.rb +236 -0
  70. data/test/attribute_list_test.rb +242 -0
  71. data/test/attributes_test.rb +65 -52
  72. data/test/blocks_test.rb +408 -260
  73. data/test/converter_test.rb +7 -7
  74. data/test/document_test.rb +60 -54
  75. data/test/extensions_test.rb +218 -32
  76. data/test/fixtures/doctime-localtime.adoc +2 -0
  77. data/test/fixtures/section-a.adoc +4 -0
  78. data/test/fixtures/subs.adoc +0 -1
  79. data/test/invoker_test.rb +56 -18
  80. data/test/links_test.rb +105 -81
  81. data/test/lists_test.rb +636 -265
  82. data/test/logger_test.rb +1 -1
  83. data/test/manpage_test.rb +140 -3
  84. data/test/paragraphs_test.rb +42 -42
  85. data/test/parser_test.rb +63 -183
  86. data/test/paths_test.rb +21 -4
  87. data/test/preamble_test.rb +9 -9
  88. data/test/reader_test.rb +78 -28
  89. data/test/sections_test.rb +273 -151
  90. data/test/substitutions_test.rb +53 -19
  91. data/test/tables_test.rb +286 -163
  92. data/test/test_helper.rb +4 -3
  93. data/test/text_test.rb +65 -65
  94. metadata +16 -21
@@ -248,7 +248,7 @@ class Table::Cell < AbstractNode
248
248
  # REVIEW feels hacky to inherit all attributes from column
249
249
  update_attributes column.attributes
250
250
  end
251
- # NOTE if attributes is defined, we know this is a psv cell; implies text should be stripped
251
+ # NOTE if attributes is defined, we know this is a psv cell; implies text needs to be stripped
252
252
  if attributes
253
253
  if attributes.empty?
254
254
  @colspan = @rowspan = nil
@@ -280,6 +280,10 @@ class Table::Cell < AbstractNode
280
280
  end
281
281
  else
282
282
  @colspan = @rowspan = nil
283
+ if cell_style == :asciidoc
284
+ asciidoc = true
285
+ inner_document_cursor = opts[:cursor]
286
+ end
283
287
  end
284
288
  # NOTE only true for non-header rows
285
289
  if asciidoc
@@ -587,14 +591,18 @@ class Table::ParserContext
587
591
  @buffer = ''
588
592
  cellspec = nil
589
593
  repeat = 1
590
- if @format == 'csv'
591
- if !cell_text.empty? && cell_text.include?('"')
592
- # this may not be perfect logic, but it hits the 99%
593
- if cell_text.start_with?('"') && cell_text.end_with?('"')
594
- # unquote
595
- cell_text = cell_text.slice(1, cell_text.length - 2).strip
594
+ if @format == 'csv' && !cell_text.empty? && cell_text.include?('"')
595
+ # this may not be perfect logic, but it hits the 99%
596
+ if cell_text.start_with?('"') && cell_text.end_with?('"')
597
+ # unquote
598
+ if (cell_text = cell_text.slice(1, cell_text.length - 2))
599
+ # trim whitespace and collapse escaped quotes
600
+ cell_text = cell_text.strip.squeeze('"')
601
+ else
602
+ logger.error message_with_context 'unclosed quote in CSV data; setting cell to empty', :source_location => @reader.cursor_at_prev_line
603
+ cell_text = ''
596
604
  end
597
-
605
+ else
598
606
  # collapse escaped quotes
599
607
  cell_text = cell_text.squeeze('"')
600
608
  end
@@ -1,3 +1,3 @@
1
1
  module Asciidoctor
2
- VERSION = '1.5.7.1'
2
+ VERSION = '1.5.8'
3
3
  end
@@ -1,13 +1,13 @@
1
1
  '\" t
2
2
  .\" Title: asciidoctor
3
3
  .\" Author: Dan Allen, Sarah White, Ryan Waldron
4
- .\" Generator: Asciidoctor 1.5.7.1
5
- .\" Date: 2018-05-10
4
+ .\" Generator: Asciidoctor 1.5.8
5
+ .\" Date: 2018-10-28
6
6
  .\" Manual: Asciidoctor Manual
7
- .\" Source: Asciidoctor 1.5.7.1
7
+ .\" Source: Asciidoctor 1.5.8
8
8
  .\" Language: English
9
9
  .\"
10
- .TH "ASCIIDOCTOR" "1" "2018-05-10" "Asciidoctor 1.5.7.1" "Asciidoctor Manual"
10
+ .TH "ASCIIDOCTOR" "1" "2018-10-28" "Asciidoctor 1.5.8" "Asciidoctor Manual"
11
11
  .ie \n(.g .ds Aq \(aq
12
12
  .el .ds Aq '
13
13
  .ss \n[.ss] 0
@@ -153,7 +153,8 @@ This option may be specified more than once.
153
153
  .sp
154
154
  \fB\-s, \-\-no\-header\-footer\fP
155
155
  .RS 4
156
- Suppress the document header and footer in the output.
156
+ Output an embeddable document, which excludes the header, the footer, and everything outside the body of the document.
157
+ This option is useful for producing documents that can be inserted into an external template.
157
158
  .RE
158
159
  .sp
159
160
  \fB\-T, \-\-template\-dir\fP=\fIDIR\fP
@@ -2,7 +2,7 @@
2
2
  Dan Allen; Sarah White; Ryan Waldron
3
3
  :doctype: manpage
4
4
  :man manual: Asciidoctor Manual
5
- :man source: Asciidoctor 1.5.7.1
5
+ :man source: Asciidoctor 1.5.8
6
6
  :page-layout: base
7
7
 
8
8
  == NAME
@@ -109,7 +109,8 @@ This option may be specified more than once.
109
109
  This option may be specified more than once.
110
110
 
111
111
  *-s, --no-header-footer*::
112
- Suppress the document header and footer in the output.
112
+ Output an embeddable document, which excludes the header, the footer, and everything outside the body of the document.
113
+ This option is useful for producing documents that can be inserted into an external template.
113
114
 
114
115
  *-T, --template-dir*=_DIR_::
115
116
  A directory containing custom converter templates that override one or more templates from the built-in set.
@@ -576,6 +576,90 @@ content
576
576
  assert_equal 'Section', result[0].title
577
577
  end
578
578
 
579
+ test 'find_by should skip node and its children if block returns :skip' do
580
+ input = <<-EOS
581
+ paragraph 1
582
+
583
+ ====
584
+ paragraph 2
585
+
586
+ term::
587
+ +
588
+ paragraph 3
589
+ ====
590
+
591
+ paragraph 4
592
+ EOS
593
+ doc = Asciidoctor.load input
594
+ result = doc.find_by do |candidate|
595
+ ctx = candidate.context
596
+ if ctx == :example
597
+ :skip
598
+ elsif ctx == :paragraph
599
+ true
600
+ end
601
+ end
602
+ refute_nil result
603
+ assert_equal 2, result.size
604
+ assert_equal :paragraph, result[0].context
605
+ assert_equal :paragraph, result[1].context
606
+ end
607
+
608
+ test 'find_by should accept node but skip its children if block returns :skip_children' do
609
+ input = <<-EOS
610
+ ====
611
+ paragraph 2
612
+
613
+ term::
614
+ +
615
+ paragraph 3
616
+ ====
617
+ EOS
618
+ doc = Asciidoctor.load input
619
+ result = doc.find_by do |candidate|
620
+ if candidate.context == :example
621
+ :skip_children
622
+ end
623
+ end
624
+ refute_nil result
625
+ assert_equal 1, result.size
626
+ assert_equal :example, result[0].context
627
+ end
628
+
629
+ test 'find_by should stop looking for blocks when StopIteration is raised' do
630
+ input = <<-EOS
631
+ paragraph 1
632
+
633
+ ====
634
+ paragraph 2
635
+
636
+ ****
637
+ paragraph 3
638
+ ****
639
+ ====
640
+
641
+ paragraph 4
642
+
643
+ * item
644
+ +
645
+ paragraph 5
646
+ EOS
647
+ doc = Asciidoctor.load input
648
+
649
+ stop_at_next = false
650
+ result = doc.find_by do |candidate|
651
+ raise StopIteration if stop_at_next
652
+ if candidate.context == :paragraph
653
+ candidate.parent.context == :sidebar ? (stop_at_next = true) : true
654
+ end
655
+ end
656
+ refute_nil result
657
+ assert_equal 3, result.size
658
+ assert_equal 'paragraph 1', result[0].content
659
+ assert_equal 'paragraph 2', result[1].content
660
+ assert_equal 'paragraph 3', result[2].content
661
+ end
662
+
579
663
  test 'find_by should only return one result when matching by id' do
580
664
  input = <<-EOS
581
665
  == Section
@@ -595,6 +679,29 @@ content
595
679
  assert_equal 'Subsection', result[0].title
596
680
  end
597
681
 
682
+ test 'find_by should stop seeking once match is found' do
683
+ input = <<-EOS
684
+ == Section
685
+
686
+ content
687
+
688
+ [#subsection]
689
+ === Subsection
690
+
691
+ [#last]
692
+ content
693
+ EOS
694
+ doc = Asciidoctor.load input
695
+ visited_last = false
696
+ result = doc.find_by(:id => 'subsection') do |candidate|
697
+ visited_last = true if candidate.id == 'last'
698
+ true
699
+ end
700
+ refute_nil result
701
+ assert_equal 1, result.size
702
+ refute visited_last
703
+ end
704
+
598
705
  test 'find_by should return an empty Array if the id criteria matches but the block argument yields false' do
599
706
  input = <<-EOS
600
707
  == Section
@@ -988,6 +1095,94 @@ text
988
1095
  end
989
1096
 
990
1097
  context 'AST' do
1098
+ test 'with no author' do
1099
+ input = <<-EOS
1100
+ = Getting Real: The Smarter, Faster, Easier Way to Build a Successful Web Application
1101
+
1102
+ Getting Real details the business, design, programming, and marketing principles of 37signals.
1103
+ EOS
1104
+
1105
+ doc = document_from_string input
1106
+ assert_equal 0, doc.authors.size
1107
+ end
1108
+
1109
+ test 'with one author' do
1110
+ input = <<-EOS
1111
+ = Getting Real: The Smarter, Faster, Easier Way to Build a Successful Web Application
1112
+ David Heinemeier Hansson <david@37signals.com>
1113
+
1114
+ Getting Real details the business, design, programming, and marketing principles of 37signals.
1115
+ EOS
1116
+
1117
+ doc = document_from_string input
1118
+ authors = doc.authors
1119
+ assert_equal 1, authors.size
1120
+ author_1 = authors[0]
1121
+ assert_equal 'david@37signals.com', author_1.email
1122
+ assert_equal 'David Heinemeier Hansson', author_1.name
1123
+ assert_equal 'David', author_1.firstname
1124
+ assert_equal 'Heinemeier', author_1.middlename
1125
+ assert_equal 'Hansson', author_1.lastname
1126
+ assert_equal 'DHH', author_1.initials
1127
+ end
1128
+
1129
+ test 'with two authors' do
1130
+ input = <<-EOS
1131
+ = Getting Real: The Smarter, Faster, Easier Way to Build a Successful Web Application
1132
+ David Heinemeier Hansson <david@37signals.com>; Jason Fried <jason@37signals.com>
1133
+
1134
+ Getting Real details the business, design, programming, and marketing principles of 37signals.
1135
+ EOS
1136
+
1137
+ doc = document_from_string input
1138
+ authors = doc.authors
1139
+ assert_equal 2, authors.size
1140
+ author_1 = authors[0]
1141
+ assert_equal 'david@37signals.com', author_1.email
1142
+ assert_equal 'David Heinemeier Hansson', author_1.name
1143
+ assert_equal 'David', author_1.firstname
1144
+ assert_equal 'Heinemeier', author_1.middlename
1145
+ assert_equal 'Hansson', author_1.lastname
1146
+ assert_equal 'DHH', author_1.initials
1147
+ author_2 = authors[1]
1148
+ assert_equal 'jason@37signals.com', author_2.email
1149
+ assert_equal 'Jason Fried', author_2.name
1150
+ assert_equal 'Jason', author_2.firstname
1151
+ assert_nil author_2.middlename
1152
+ assert_equal 'Fried', author_2.lastname
1153
+ assert_equal 'JF', author_2.initials
1154
+ end
1155
+
1156
+ test 'with authors as attributes' do
1157
+ input = <<-EOS
1158
+ = Getting Real: The Smarter, Faster, Easier Way to Build a Successful Web Application
1159
+ :author_1: David Heinemeier Hansson
1160
+ :email_1: david@37signals.com
1161
+ :author_2: Jason Fried
1162
+ :email_2: jason@37signals.com
1163
+
1164
+ Getting Real details the business, design, programming, and marketing principles of 37signals.
1165
+ EOS
1166
+
1167
+ doc = document_from_string input
1168
+ authors = doc.authors
1169
+ assert_equal 2, authors.size
1170
+ author_1 = authors[0]
1171
+ assert_equal 'david@37signals.com', author_1.email
1172
+ assert_equal 'David Heinemeier Hansson', author_1.name
1173
+ assert_equal 'David', author_1.firstname
1174
+ assert_equal 'Heinemeier', author_1.middlename
1175
+ assert_equal 'Hansson', author_1.lastname
1176
+ assert_equal 'DHH', author_1.initials
1177
+ author_2 = authors[1]
1178
+ assert_equal 'jason@37signals.com', author_2.email
1179
+ assert_equal 'Jason Fried', author_2.name
1180
+ assert_equal 'Jason', author_2.firstname
1181
+ assert_nil author_2.middlename
1182
+ assert_equal 'Fried', author_2.lastname
1183
+ assert_equal 'JF', author_2.initials
1184
+ end
1185
+
991
1186
  test 'should not crash if nil cell text is passed to Cell constructor' do
992
1187
  input = <<-EOS
993
1188
  |===
@@ -1000,5 +1195,46 @@ text
1000
1195
  assert_same Asciidoctor::AbstractNode::NORMAL_SUBS, cell.subs
1001
1196
  assert_equal '', cell.text
1002
1197
  end
1198
+
1199
+ test 'should set option on node when set_option is called' do
1200
+ input = <<-EOS
1201
+ . three
1202
+ . two
1203
+ . one
1204
+ EOS
1205
+
1206
+ block = (document_from_string input).blocks[0]
1207
+ assert block.set_option('reversed')
1208
+ refute block.set_option('reversed')
1209
+ assert block.option?('reversed')
1210
+ assert_equal '', block.attributes['reversed-option']
1211
+ assert_equal 'reversed', block.attributes['options']
1212
+ end
1213
+
1214
+ test 'should append option to existing options' do
1215
+ input = <<-EOS
1216
+ [%fancy]
1217
+ . three
1218
+ . two
1219
+ . one
1220
+ EOS
1221
+
1222
+ block = (document_from_string input).blocks[0]
1223
+ assert block.set_option('reversed')
1224
+ assert_equal 'fancy,reversed', block.attributes['options']
1225
+ end
1226
+
1227
+ test 'should not append option if option is already set' do
1228
+ input = <<-EOS
1229
+ [%reversed]
1230
+ . three
1231
+ . two
1232
+ . one
1233
+ EOS
1234
+
1235
+ block = (document_from_string input).blocks[0]
1236
+ refute block.set_option('reversed')
1237
+ assert_equal 'reversed', block.attributes['options']
1238
+ end
1003
1239
  end
1004
1240
  end
@@ -0,0 +1,242 @@
1
+ # encoding: UTF-8
2
+ unless defined? ASCIIDOCTOR_PROJECT_DIR
3
+ $: << File.dirname(__FILE__); $:.uniq!
4
+ require 'test_helper'
5
+ end
6
+
7
+ context 'AttributeList' do
8
+ test 'collect unnamed attribute' do
9
+ attributes = {}
10
+ line = 'quote'
11
+ expected = {1 => 'quote'}
12
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
13
+ assert_equal expected, attributes
14
+ end
15
+
16
+ test 'collect unnamed attribute double-quoted' do
17
+ attributes = {}
18
+ line = '"quote"'
19
+ expected = {1 => 'quote'}
20
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
21
+ assert_equal expected, attributes
22
+ end
23
+
24
+ test 'collect empty unnamed attribute double-quoted' do
25
+ attributes = {}
26
+ line = '""'
27
+ expected = {1 => ''}
28
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
29
+ assert_equal expected, attributes
30
+ end
31
+
32
+ test 'collect unnamed attribute double-quoted containing escaped quote' do
33
+ attributes = {}
34
+ line = '"ba\"zaar"'
35
+ expected = {1 => 'ba"zaar'}
36
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
37
+ assert_equal expected, attributes
38
+ end
39
+
40
+ test 'collect unnamed attribute single-quoted' do
41
+ attributes = {}
42
+ line = '\'quote\''
43
+ expected = {1 => 'quote'}
44
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
45
+ assert_equal expected, attributes
46
+ end
47
+
48
+ test 'collect empty unnamed attribute single-quoted' do
49
+ attributes = {}
50
+ line = '\'\''
51
+ expected = {1 => ''}
52
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
53
+ assert_equal expected, attributes
54
+ end
55
+
56
+ test 'collect isolated single quote positional attribute' do
57
+ attributes = {}
58
+ line = '\''
59
+ expected = { 1 => '\'' }
60
+ doc = empty_document
61
+ def doc.apply_subs *args
62
+ fail 'apply_subs should not be called'
63
+ end
64
+ Asciidoctor::AttributeList.new(line, doc).parse_into(attributes)
65
+ assert_equal expected, attributes
66
+ end
67
+
68
+ test 'collect isolated single quote attribute value' do
69
+ attributes = {}
70
+ line = 'name=\''
71
+ expected = { 'name' => '\'' }
72
+ doc = empty_document
73
+ def doc.apply_subs *args
74
+ fail 'apply_subs should not be called'
75
+ end
76
+ Asciidoctor::AttributeList.new(line, doc).parse_into(attributes)
77
+ assert_equal expected, attributes
78
+ end
79
+
80
+ test 'collect attribute value as is if it has only leading single quote' do
81
+ attributes = {}
82
+ line = 'name=\'{val}'
83
+ expected = { 'name' => '\'{val}' }
84
+ doc = empty_document :attributes => { 'val' => 'val' }
85
+ def doc.apply_subs *args
86
+ fail 'apply_subs should not be called'
87
+ end
88
+ Asciidoctor::AttributeList.new(line, doc).parse_into(attributes)
89
+ assert_equal expected, attributes
90
+ end
91
+
92
+ test 'collect unnamed attribute single-quoted containing escaped quote' do
93
+ attributes = {}
94
+ line = '\'ba\\\'zaar\''
95
+ expected = {1 => 'ba\'zaar'}
96
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
97
+ assert_equal expected, attributes
98
+ end
99
+
100
+ test 'collect unnamed attribute with dangling delimiter' do
101
+ attributes = {}
102
+ line = 'quote , '
103
+ expected = {1 => 'quote'}
104
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
105
+ assert_equal expected, attributes
106
+ end
107
+
108
+ test 'collect unnamed attribute in second position after empty attribute' do
109
+ attributes = {}
110
+ line = ', John Smith'
111
+ expected = {1 => nil, 2 => 'John Smith'}
112
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
113
+ assert_equal expected, attributes
114
+ end
115
+
116
+ test 'collect unnamed attributes' do
117
+ attributes = {}
118
+ line = 'first, second one, third'
119
+ expected = {1 => 'first', 2 => 'second one', 3 => 'third'}
120
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
121
+ assert_equal expected, attributes
122
+ end
123
+
124
+ test 'collect named attribute' do
125
+ attributes = {}
126
+ line = 'foo=bar'
127
+ expected = {'foo' => 'bar'}
128
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
129
+ assert_equal expected, attributes
130
+ end
131
+
132
+ test 'collect named attribute double-quoted' do
133
+ attributes = {}
134
+ line = 'foo="bar"'
135
+ expected = {'foo' => 'bar'}
136
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
137
+ assert_equal expected, attributes
138
+ end
139
+
140
+ test 'collect named attribute with double-quoted empty value' do
141
+ attributes = {}
142
+ line = 'height=100,caption="",link="images/octocat.png"'
143
+ expected = {'height' => '100', 'caption' => '', 'link' => 'images/octocat.png'}
144
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
145
+ assert_equal expected, attributes
146
+ end
147
+
148
+ test 'collect named attribute single-quoted' do
149
+ attributes = {}
150
+ line = 'foo=\'bar\''
151
+ expected = {'foo' => 'bar'}
152
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
153
+ assert_equal expected, attributes
154
+ end
155
+
156
+ test 'collect named attribute with single-quoted empty value' do
157
+ attributes = {}
158
+ line = %(height=100,caption='',link='images/octocat.png')
159
+ expected = {'height' => '100', 'caption' => '', 'link' => 'images/octocat.png'}
160
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
161
+ assert_equal expected, attributes
162
+ end
163
+
164
+ test 'collect single named attribute with empty value' do
165
+ attributes = {}
166
+ line = 'foo='
167
+ expected = {'foo' => ''}
168
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
169
+ assert_equal expected, attributes
170
+ end
171
+
172
+ test 'collect single named attribute with empty value when followed by other attributes' do
173
+ attributes = {}
174
+ line = 'foo=,bar=baz'
175
+ expected = {'foo' => '', 'bar' => 'baz'}
176
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
177
+ assert_equal expected, attributes
178
+ end
179
+
180
+ test 'collect named attributes unquoted' do
181
+ attributes = {}
182
+ line = 'first=value, second=two, third=3'
183
+ expected = {'first' => 'value', 'second' => 'two', 'third' => '3'}
184
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
185
+ assert_equal expected, attributes
186
+ end
187
+
188
+ test 'collect named attributes quoted' do
189
+ attributes = {}
190
+ line = %(first='value', second="value two", third=three)
191
+ expected = {'first' => 'value', 'second' => 'value two', 'third' => 'three'}
192
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
193
+ assert_equal expected, attributes
194
+ end
195
+
196
+ test 'collect named attributes quoted containing non-semantic spaces' do
197
+ attributes = {}
198
+ line = %( first = 'value', second ="value two" , third= three )
199
+ expected = {'first' => 'value', 'second' => 'value two', 'third' => 'three'}
200
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
201
+ assert_equal expected, attributes
202
+ end
203
+
204
+ test 'collect mixed named and unnamed attributes' do
205
+ attributes = {}
206
+ line = %(first, second="value two", third=three, Sherlock Holmes)
207
+ expected = {1 => 'first', 'second' => 'value two', 'third' => 'three', 4 => 'Sherlock Holmes'}
208
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
209
+ assert_equal expected, attributes
210
+ end
211
+
212
+ test 'collect options attribute' do
213
+ attributes = {}
214
+ line = %(quote, options='opt1,opt2 , opt3')
215
+ expected = {1 => 'quote', 'options' => 'opt1,opt2,opt3', 'opt1-option' => '', 'opt2-option' => '', 'opt3-option' => ''}
216
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
217
+ assert_equal expected, attributes
218
+ end
219
+
220
+ test 'collect opts attribute as options' do
221
+ attributes = {}
222
+ line = %(quote, opts='opt1,opt2 , opt3')
223
+ expected = {1 => 'quote', 'options' => 'opt1,opt2,opt3', 'opt1-option' => '', 'opt2-option' => '', 'opt3-option' => ''}
224
+ Asciidoctor::AttributeList.new(line).parse_into(attributes)
225
+ assert_equal expected, attributes
226
+ end
227
+
228
+ test 'collect and rekey unnamed attributes' do
229
+ attributes = {}
230
+ line = 'first, second one, third, fourth'
231
+ expected = {1 => 'first', 2 => 'second one', 3 => 'third', 4 => 'fourth', 'a' => 'first', 'b' => 'second one', 'c' => 'third'}
232
+ Asciidoctor::AttributeList.new(line).parse_into(attributes, ['a', 'b', 'c'])
233
+ assert_equal expected, attributes
234
+ end
235
+
236
+ test 'rekey positional attributes' do
237
+ attributes = {1 => 'source', 2 => 'java'}
238
+ expected = {1 => 'source', 2 => 'java', 'style' => 'source', 'language' => 'java'}
239
+ Asciidoctor::AttributeList.rekey(attributes, ['style', 'language', 'linenums'])
240
+ assert_equal expected, attributes
241
+ end
242
+ end