kramdown-asciidoc 1.0.0.alpha.12 → 1.0.0.alpha.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +61 -2
  3. data/Gemfile +0 -5
  4. data/README.adoc +67 -5
  5. data/kramdown-asciidoc.gemspec +2 -1
  6. data/lib/kramdown-asciidoc.rb +1 -0
  7. data/lib/kramdown-asciidoc/api.rb +7 -4
  8. data/lib/kramdown-asciidoc/cli.rb +9 -1
  9. data/lib/kramdown-asciidoc/converter.rb +104 -21
  10. data/lib/kramdown-asciidoc/core_ext/regexp/is_match.rb +5 -3
  11. data/lib/kramdown-asciidoc/kramdown_ext/parser/base.rb +7 -0
  12. data/lib/kramdown-asciidoc/version.rb +1 -1
  13. data/lib/kramdown-asciidoc/writer.rb +16 -22
  14. data/spec/api_spec.rb +70 -0
  15. data/spec/cli_spec.rb +38 -6
  16. data/spec/scenarios/attributes/sorting.adoc +6 -0
  17. data/spec/scenarios/attributes/sorting.md +3 -0
  18. data/spec/scenarios/attributes/sorting.opts +3 -0
  19. data/spec/scenarios/codeblock/dlist-like.adoc +4 -0
  20. data/spec/scenarios/codeblock/dlist-like.md +2 -0
  21. data/spec/scenarios/codeblock/list-like.adoc +5 -0
  22. data/spec/scenarios/codeblock/list-like.md +3 -0
  23. data/spec/scenarios/heading/anchor-with-dot.adoc +2 -0
  24. data/spec/scenarios/heading/anchor-with-dot.md +1 -0
  25. data/spec/scenarios/heading/{auto-id-prefix.adoc → auto_ids/auto-id-prefix.adoc} +0 -0
  26. data/spec/scenarios/heading/{auto-id-prefix.md → auto_ids/auto-id-prefix.md} +0 -0
  27. data/spec/scenarios/heading/{auto-id-prefix.opts → auto_ids/auto-id-prefix.opts} +1 -1
  28. data/spec/scenarios/heading/auto_ids/character-reference.adoc +8 -0
  29. data/spec/scenarios/heading/auto_ids/character-reference.md +5 -0
  30. data/spec/scenarios/heading/{auto-ids.opts → auto_ids/character-reference.opts} +0 -0
  31. data/spec/scenarios/heading/{no-auto-ids.adoc → auto_ids/disabled.adoc} +0 -0
  32. data/spec/scenarios/heading/{no-auto-ids.md → auto_ids/disabled.md} +0 -0
  33. data/spec/scenarios/heading/{no-auto-ids.opts → auto_ids/disabled.opts} +0 -0
  34. data/spec/scenarios/heading/auto_ids/dot-separator.adoc +2 -0
  35. data/spec/scenarios/heading/auto_ids/dot-separator.md +1 -0
  36. data/spec/scenarios/heading/auto_ids/dot-separator.opts +2 -0
  37. data/spec/scenarios/heading/{auto-ids.adoc → auto_ids/explicit-id.adoc} +4 -1
  38. data/spec/scenarios/heading/{auto-ids.md → auto_ids/explicit-id.md} +3 -1
  39. data/spec/scenarios/heading/auto_ids/explicit-id.opts +1 -0
  40. data/spec/scenarios/heading/auto_ids/kramdown-input-character-reference.adoc +8 -0
  41. data/spec/scenarios/heading/auto_ids/kramdown-input-character-reference.md +5 -0
  42. data/spec/scenarios/heading/auto_ids/kramdown-input-character-reference.opts +2 -0
  43. data/spec/scenarios/heading/auto_ids/kramdown-input.adoc +14 -0
  44. data/spec/scenarios/heading/auto_ids/kramdown-input.md +9 -0
  45. data/spec/scenarios/heading/auto_ids/kramdown-input.opts +2 -0
  46. data/spec/scenarios/heading/auto_ids/no-ids.adoc +14 -0
  47. data/spec/scenarios/heading/auto_ids/no-ids.md +9 -0
  48. data/spec/scenarios/heading/auto_ids/no-ids.opts +1 -0
  49. data/spec/scenarios/heading/lazy_ids/auto-ids-sync-id-attrs.adoc +7 -0
  50. data/spec/scenarios/heading/lazy_ids/auto-ids-sync-id-attrs.md +5 -0
  51. data/spec/scenarios/heading/lazy_ids/auto-ids-sync-id-attrs.opts +5 -0
  52. data/spec/scenarios/heading/lazy_ids/auto-ids.adoc +10 -0
  53. data/spec/scenarios/heading/lazy_ids/auto-ids.md +7 -0
  54. data/spec/scenarios/heading/lazy_ids/auto-ids.opts +4 -0
  55. data/spec/scenarios/heading/lazy_ids/explicit-ids-dot-separator.adoc +9 -0
  56. data/spec/scenarios/heading/lazy_ids/explicit-ids-dot-separator.md +7 -0
  57. data/spec/scenarios/heading/lazy_ids/explicit-ids-dot-separator.opts +3 -0
  58. data/spec/scenarios/heading/lazy_ids/explicit-ids-empty-prefix.adoc +9 -0
  59. data/spec/scenarios/heading/lazy_ids/explicit-ids-empty-prefix.md +7 -0
  60. data/spec/scenarios/heading/lazy_ids/explicit-ids-empty-prefix.opts +3 -0
  61. data/spec/scenarios/heading/lazy_ids/explicit-ids-empty-separator.adoc +9 -0
  62. data/spec/scenarios/heading/lazy_ids/explicit-ids-empty-separator.md +7 -0
  63. data/spec/scenarios/heading/lazy_ids/explicit-ids-empty-separator.opts +3 -0
  64. data/spec/scenarios/heading/lazy_ids/explicit-ids-zero-separator.adoc +7 -0
  65. data/spec/scenarios/heading/lazy_ids/explicit-ids-zero-separator.md +5 -0
  66. data/spec/scenarios/heading/lazy_ids/explicit-ids-zero-separator.opts +3 -0
  67. data/spec/scenarios/heading/lazy_ids/explicit-ids.adoc +15 -0
  68. data/spec/scenarios/heading/lazy_ids/explicit-ids.md +13 -0
  69. data/spec/scenarios/heading/lazy_ids/explicit-ids.opts +1 -0
  70. data/spec/scenarios/html_element/image-with-id.adoc +2 -0
  71. data/spec/scenarios/html_element/image-with-id.md +1 -0
  72. metadata +131 -24
  73. data/spec/writer_spec.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64f9079115320afb426fbb449cd0b6163b49fbdeb7ae2dc07407eb37524c787a
4
- data.tar.gz: e2b4a086c0ec8248970d4d8f121b9642b8a9665510b69fe658f9311a7f96fa2f
3
+ metadata.gz: f703fb6eabd74ef5641f298a7d7bc2fadce80e5fe9b97d1965abad29a5f5a4d6
4
+ data.tar.gz: 18c3c1f8f4f2006381b83561badf425e56762216c92d64cc42aae640058ded82
5
5
  SHA512:
6
- metadata.gz: 2a537db76f1c5406c9a991e442fa366a121907c3b3aeb813434da304f2ec37d0773c1a91b72224ff2ab28f7ad8bc8f9884c842dce0d4228f31a44f4aca889a9e
7
- data.tar.gz: fd86b40db1f4f2b22d99ae6295a8a78173f0e8039045224306b143f1d6fd96e00e384c6d9fea4873ccad4616771fa29a96b8cb01005e129315d19c729b7a8cb6
6
+ metadata.gz: 1f51c28d5cfa7d9c7fb8c1f32e5502a052b0e0030f782c0f4e4f6814c792a36d9f9e03663013d84b338021e0524fefb0fcf54b8a381078f22ebb56cd1f645f06
7
+ data.tar.gz: 768357f79d874791c79aa6f4a0c1df5f3eaa3fff1648b03f1465d5648ba64ab62279823a2e9133950e42904fdb75c84462db830a1ceecfc2e50d1d60f6b7fafe
@@ -5,6 +5,26 @@
5
5
  This document provides a high-level view of the changes to {project-name} by release.
6
6
  For a detailed view of what has changed, refer to the {uri-repo}/commits/master[commit history] on GitHub.
7
7
 
8
+ == 1.0.0.alpha.13 (2018-08-21) - @mojavelinux
9
+
10
+ === Added
11
+
12
+ * drop ID on section title if matches auto-generated value when :lazy_ids API option / --lazy-ids CLI option is set (#16)
13
+ * auto-generate IDs for section titles when :auto_ids option is set and :input is not GFM
14
+ * add :postprocess callback option to `convert` and `convert_file` API methods (#53)
15
+
16
+ === Changed
17
+
18
+ * automatically set idprefix, if necessary, based on value of :auto_id_prefix option when :auto_ids options is set (#16)
19
+ * don't modify value of :attributes option passed to `convert` and `convert_file` API methods
20
+ * define ID containing a dot using longhand attribute assignment (#59)
21
+ * extract raw text consistently regardless of input flavor (#56)
22
+ * sort attributes in document header (#55)
23
+
24
+ === Fixed
25
+
26
+ * enclose codeblock content in literal block delimiters if content starts with a list marker (#57)
27
+
8
28
  == 1.0.0.alpha.12 (2018-08-11) - @mojavelinux
9
29
 
10
30
  === Added
@@ -23,6 +43,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
23
43
  * break on all terminal punctuation (period, question mark, and exclamation mark) when ventilating prose (#51)
24
44
  * consolidated logic in CLI by further delegating to API
25
45
 
46
+ === Details
47
+
48
+ {uri-repo}/releases/tag/v1.0.0.alpha.12[git tag] | {uri-repo}/compare/v1.0.0.alpha.11\...v1.0.0.alpha.12[full diff]
49
+
26
50
  == 1.0.0.alpha.11 (2018-08-02) - @mojavelinux
27
51
 
28
52
  === Added
@@ -42,6 +66,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
42
66
  * use unconstrained codespan if bounded on either side by a smart quote
43
67
  * ignore auto-generated ID if heading has an explicit inline anchor
44
68
 
69
+ === Details
70
+
71
+ {uri-repo}/releases/tag/v1.0.0.alpha.11[git tag] | {uri-repo}/compare/v1.0.0.alpha.10\...v1.0.0.alpha.11[full diff]
72
+
45
73
  == 1.0.0.alpha.10 (2018-07-16) - @mojavelinux
46
74
 
47
75
  === Added
@@ -60,6 +88,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
60
88
 
61
89
  * insert blank line above list continuation to attach to parent list item (#27)
62
90
 
91
+ === Details
92
+
93
+ {uri-repo}/releases/tag/v1.0.0.alpha.10[git tag] | {uri-repo}/compare/v1.0.0.alpha.9\...v1.0.0.alpha.10[full diff]
94
+
63
95
  == 1.0.0.alpha.9 (2018-07-10) - @mojavelinux
64
96
 
65
97
  === Changed
@@ -69,6 +101,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
69
101
  * don't add newline after period at start of line when producing ventilated prose
70
102
  * use :imagesdir API option or --imagesdir CLI option to set implicit imagesdir instead of attribute
71
103
 
104
+ === Details
105
+
106
+ {uri-repo}/releases/tag/v1.0.0.alpha.9[git tag] | {uri-repo}/compare/v1.0.0.alpha.8\...v1.0.0.alpha.9[full diff]
107
+
72
108
  == 1.0.0.alpha.8 (2018-07-03) - @mojavelinux
73
109
 
74
110
  === Added
@@ -80,6 +116,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
80
116
  * don't escape double hyphen in codespan unless surrounded by spaces or word chars
81
117
  * treat leading specialchar (<, >, or &) as a word character (since it gets converted to a char reference)
82
118
 
119
+ === Details
120
+
121
+ {uri-repo}/releases/tag/v1.0.0.alpha.8[git tag] | {uri-repo}/compare/v1.0.0.alpha.7\...v1.0.0.alpha.8[full diff]
122
+
83
123
  == 1.0.0.alpha.7 (2018-07-02) - @mojavelinux
84
124
 
85
125
  === Added
@@ -100,6 +140,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
100
140
  * replace .md extension with .adoc in text of interdoc xref
101
141
  * replace a non-breaking space with a single space instead of \{nbsp}
102
142
 
143
+ === Details
144
+
145
+ {uri-repo}/releases/tag/v1.0.0.alpha.7[git tag] | {uri-repo}/compare/v1.0.0.alpha.6\...v1.0.0.alpha.7[full diff]
146
+
103
147
  == 1.0.0.alpha.6 (2018-06-26) - @mojavelinux
104
148
 
105
149
  === Added
@@ -116,6 +160,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
116
160
  * use writer to track list nesting level
117
161
  * fix warnings
118
162
 
163
+ === Details
164
+
165
+ {uri-repo}/releases/tag/v1.0.0.alpha.6[git tag] | {uri-repo}/compare/v1.0.0.alpha.5\...v1.0.0.alpha.6[full diff]
166
+
119
167
  == 1.0.0.alpha.5 (2018-06-19) - @mojavelinux
120
168
 
121
169
  === Added
@@ -131,6 +179,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
131
179
  * use separate list level for dl
132
180
  * fold description list item to one line if primary text is a single line
133
181
 
182
+ === Details
183
+
184
+ {uri-repo}/releases/tag/v1.0.0.alpha.5[git tag] | {uri-repo}/compare/v1.0.0.alpha.4\...v1.0.0.alpha.5[full diff]
185
+
134
186
  == 1.0.0.alpha.4 (2018-06-12) - @mojavelinux
135
187
 
136
188
  === Added
@@ -156,6 +208,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
156
208
  * upgrade kramdown to 1.17.0
157
209
  * use correct casing for kramdown in README and library metadata
158
210
 
211
+ === Details
212
+
213
+ {uri-repo}/releases/tag/v1.0.0.alpha.4[git tag] | {uri-repo}/compare/v1.0.0.alpha.3\...v1.0.0.alpha.4[full diff]
214
+
159
215
  == 1.0.0.alpha.3 (2018-05-31) - @mojavelinux
160
216
 
161
217
  === Added
@@ -169,6 +225,10 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
169
225
 
170
226
  * replace ndash symbol with \-- instead of \&#8211;
171
227
 
228
+ === Details
229
+
230
+ {uri-repo}/releases/tag/v1.0.0.alpha.3[git tag] | {uri-repo}/compare/v1.0.0.alpha.2\...v1.0.0.alpha.3[full diff]
231
+
172
232
  == 1.0.0.alpha.2 (2018-05-24) - @mojavelinux
173
233
 
174
234
  === Added
@@ -195,8 +255,7 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[
195
255
 
196
256
  === Details
197
257
 
198
- {uri-repo}/releases/tag/v1.0.0.alpha.2[git tag] |
199
- {uri-repo}/compare/v1.0.0.alpha.1...v1.0.0.alpha.2[full diff]
258
+ {uri-repo}/releases/tag/v1.0.0.alpha.2[git tag] | {uri-repo}/compare/v1.0.0.alpha.1\...v1.0.0.alpha.2[full diff]
200
259
 
201
260
  == 1.0.0.alpha.1 (2018-05-22) - @mojavelinux
202
261
 
data/Gemfile CHANGED
@@ -1,8 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
5
- group :coverage do
6
- gem 'deep-cover', git: 'https://github.com/mojavelinux/deep-cover', branch: 'no-cli', require: false
7
- gem 'sass', require: false unless ENV['CI']
8
- end
@@ -1,6 +1,6 @@
1
1
  = {project-name} (Markdown to AsciiDoc)
2
2
  Dan Allen <https://github.com/mojavelinux>
3
- v1.0.0.alpha.12, 2018-08-11
3
+ v1.0.0.alpha.13, 2018-08-21
4
4
  // Aliases:
5
5
  :project-name: Kramdown AsciiDoc
6
6
  :project-handle: kramdown-asciidoc
@@ -25,6 +25,7 @@ endif::[]
25
25
  :uri-kramdown: https://kramdown.gettalong.org
26
26
  :uri-rvm: https://rvm.io
27
27
  :uri-rvm-install: https://rvm.io/rvm/install
28
+ :uri-docs-api: https://www.rubydoc.info/github/asciidoctor/kramdown-asciidoc/master
28
29
  :uri-ci-appveyor: https://ci.appveyor.com/project/asciidoctor/kramdown-asciidoc
29
30
  :img-ci-appveyor: https://ci.appveyor.com/api/projects/status/2pwvdbcoeux1ifb5/branch/master?svg=true
30
31
  :uri-ci-travis: https://travis-ci.org/asciidoctor/kramdown-asciidoc
@@ -92,7 +93,7 @@ For example, you can inject attributes (key/value pairs) into the header of the
92
93
 
93
94
  $ kramdoc -a product-name="ACME Cloud" -a hide-uri-scheme sample.md
94
95
 
95
- Another use for attributes is setting the shared images directory, which is coverd in the next section.
96
+ Another use for attributes is setting the shared images directory, which is covered in the next section.
96
97
 
97
98
  == Configure shared images directory
98
99
 
@@ -136,9 +137,34 @@ image::octocat.png[Octocat]
136
137
 
137
138
  In this scenario, you may need to pass the `imagesdir` attribute to the AsciiDoc processor when converting the output document so the image is resolved, depending on where the image is stored.
138
139
 
140
+ == Auto-generate IDs
141
+
142
+ You can configure kramdoc to automatically generate explicit IDs for each section title (aka heading) that doesn't already have an ID assigned to it (in the Markdown source).
143
+ To do so, simply enable the `--auto-ids` flag:
144
+
145
+ $ kramdoc --auto-ids sample.md
146
+
147
+ By default, kramdoc does not add a prefix to the generated ID and uses `-` as the separator / replacement character.
148
+ You can change these values using the `--auto-id-prefix` and `--auto-id-separator` options, respectively:
149
+
150
+ $ kramdoc --auto-ids --auto-id-prefix=_ --auto-id-separator=_ sample.md
151
+
152
+ Since the AsciiDoc processor generates an ID for any section title that doesn't have one by default, you may decide you want to drop any ID which matches its auto-generated value.
153
+ You can enable this behavior by adding the `--lazy-ids` flag:
154
+
155
+ $ kramdoc --lazy-ids sample.md
156
+
157
+ The catch is that kramdown/kramdoc and AsciiDoc don't use the same prefix and separator when generating IDs.
158
+ So it's necessary to sync them.
159
+ The simplest way is to set the `--auto-id-prefix` and `--auto-id-separator` values to match those used by AsciiDoc.
160
+
161
+ $ kramdoc --lazy-ids --auto-id-prefix=_ --auto-id-separator=_ sample.md
162
+
163
+ If these values do not match the defaults in AsciiDoc, the `idprefix` and/or `idseparator` attributes will be assigned explicitly in the generated document.
164
+
139
165
  == API
140
166
 
141
- In additional to the command-line interface, {project-name} also provides a porcelain API.
167
+ In additional to the command-line interface, {project-name} also provides a porcelain API (see {uri-docs-api}[API docs]).
142
168
  We use the term "`porcelain`" because the API hides the details of registering the converter, preprocessing the Markdown document, parsing the document with kramdown, and calling the converter method to transform the parse tree to AsciiDoc.
143
169
 
144
170
  The API consists of two static methods:
@@ -194,6 +220,8 @@ Kramdoc.convert markdown, to: 'result.adoc'
194
220
 
195
221
  The input string is automatically converted to UTF-8.
196
222
 
223
+ For more information about the API, refer to the {uri-docs-api}[API documentation].
224
+
197
225
  == Development
198
226
 
199
227
  To help develop {project-name}, or to simply test-drive the development version, you need to retrieve the source from GitHub.
@@ -242,7 +270,7 @@ Then, use the `bundle` command to install the project dependencies under the pro
242
270
 
243
271
  NOTE: You must invoke `bundle` from the project's root directory so it can locate the [.path]_Gemfile_.
244
272
 
245
- === Run the Tests
273
+ === Run the tests
246
274
 
247
275
  The test suite is located in the [.path]_spec_ directory.
248
276
  The tests are all based on RSpec.
@@ -264,7 +292,7 @@ To run all the scenarios, point RSpec at the spec file:
264
292
 
265
293
  $ bundle exec rspec spec/scenario_spec.rb
266
294
 
267
- ==== Run Individual Tests
295
+ ==== Run individual tests
268
296
 
269
297
  If you only want to run a single test, or a group of tests, you can do so by tagging the test cases, then filtering the test run using that tag.
270
298
 
@@ -291,6 +319,40 @@ Run RSpec with the example filter:
291
319
 
292
320
  RSpec will only run the specifications that have a description containing the text `wrap`.
293
321
 
322
+ === Generate code coverage
323
+
324
+ To generate a code coverage report when running tests using simplecov, set the `COVERAGE` environment variable as follows when running the tests:
325
+
326
+ $ COVERAGE=true bundle exec rake
327
+
328
+ You'll see a total coverage score as well as a link to the HTML report in the output.
329
+ The HTML report helps you understand which lines and branches were missed, if any.
330
+
331
+ Despite being fast, the downside of using simplecov is that it misses branches.
332
+ You can use deep-cover to generate a more thorough report.
333
+ To do so, set the `COVERAGE` environment variable as follows when running the tests:
334
+
335
+ $ COVERAGE=deep bundle exec rake
336
+
337
+ You'll see a total coverage score, a detailed coverage report, and a link to HTML report in the output.
338
+ The HTML report helps you understand which lines and branches were missed, if any.
339
+
340
+ As an alternative to deep cover's native HTML reporter, you can also use istanbul / nyc.
341
+ First, you'll need to have the `nyc` command available on your system:
342
+
343
+ $ npm install -g nyc
344
+
345
+ or
346
+
347
+ $ yarn global add nyc
348
+
349
+ Next, in addition to the `COVERAGE` environment variable, also set the `DEEP_COVER_REPORTER` environment variable as follows when running the tests:
350
+
351
+ $ COVERAGE=deep DEEP_COVER_REPORTER=istanbul bundle exec rake
352
+
353
+ You'll see a total coverage score, a detailed coverage report, and a link to HTML report in the output.
354
+ The HTML report helps you understand which lines and branches were missed, if any.
355
+
294
356
  === Usage
295
357
 
296
358
  When running the `kramdoc` command from source, you must prefix the command with `bundle exec`:
@@ -35,7 +35,8 @@ Gem::Specification.new do |s|
35
35
  #s.extra_rdoc_files = ['CHANGELOG.adoc', 'LICENSE.adoc']
36
36
 
37
37
  s.add_runtime_dependency 'kramdown', '~> 1.17.0'
38
+ s.add_development_dependency 'deep-cover-core', '~> 0.6.3'
38
39
  s.add_development_dependency 'rake', '~> 12.3.1'
39
- s.add_development_dependency 'rspec', '~> 3.7.0'
40
+ s.add_development_dependency 'rspec', '~> 3.8.0'
40
41
  s.add_development_dependency 'simplecov', '~> 0.16.1'
41
42
  end
@@ -1,4 +1,5 @@
1
1
  require 'kramdown'
2
+ require_relative 'kramdown-asciidoc/kramdown_ext/parser/base'
2
3
  require_relative 'kramdown-asciidoc/kramdown_ext/parser/html'
3
4
  require_relative 'kramdown-asciidoc/core_ext/regexp/is_match'
4
5
  require_relative 'kramdown-asciidoc/api'
@@ -4,16 +4,19 @@ module Kramdown; module AsciiDoc
4
4
  UTF_8 = ::Encoding::UTF_8
5
5
 
6
6
  def self.convert markdown, opts = {}
7
- unless (opts.delete :encode) == false || (markdown.encoding == UTF_8 && !(markdown.include? CR))
7
+ unless opts[:encode] == false || (markdown.encoding == UTF_8 && !(markdown.include? CR))
8
8
  markdown = markdown.encode UTF_8, universal_newline: true
9
9
  end
10
10
  markdown = markdown.rstrip
11
11
  markdown = markdown.slice 1, markdown.length while markdown.start_with? LF
12
- # QUESTION should we .dup?
13
- attributes = (opts[:attributes] ||= {})
12
+ parser_opts = ::Kramdown::AsciiDoc::DEFAULT_PARSER_OPTS.merge opts
13
+ attributes = (parser_opts[:attributes] = (parser_opts[:attributes] || {}).dup)
14
14
  markdown = ::Kramdown::AsciiDoc.extract_front_matter markdown, attributes
15
15
  markdown = ::Kramdown::AsciiDoc.replace_toc markdown, attributes
16
- asciidoc = (::Kramdown::Document.new markdown, (::Kramdown::AsciiDoc::DEFAULT_PARSER_OPTS.merge opts)).to_asciidoc
16
+ asciidoc = (kramdown_doc = ::Kramdown::Document.new markdown, parser_opts).to_asciidoc
17
+ if (postprocess = opts[:postprocess])
18
+ asciidoc = (postprocess.arity == 1 ? postprocess[asciidoc] : postprocess[asciidoc, kramdown_doc]) || asciidoc
19
+ end
17
20
  asciidoc += LF unless asciidoc.empty?
18
21
  if (to = opts[:to])
19
22
  if ::Pathname === to || (!(to.respond_to? :write) && (to = ::Pathname.new to.to_s))
@@ -53,10 +53,18 @@ module Kramdown; module AsciiDoc
53
53
  options[:auto_ids] = auto_ids
54
54
  end
55
55
 
56
- opts.on '--auto-id-prefix=STRING', 'Set the prefix to add to auto-generated section title IDs' do |string|
56
+ opts.on '--auto-id-prefix=STRING', 'Set the prefix to use for auto-generated section title IDs' do |string|
57
57
  options[:auto_id_prefix] = string
58
58
  end
59
59
 
60
+ opts.on '--auto-id-separator=CHAR', 'Set the separator char to use for auto-generated section title IDs' do |char|
61
+ options[:auto_id_separator] = char
62
+ end
63
+
64
+ opts.on '--lazy-ids', 'Set whether to drop IDs that match value of auto-generated ID' do |lazy_ids|
65
+ options[:lazy_ids] = lazy_ids
66
+ end
67
+
60
68
  opts.on '--[no-]auto-links', 'Set whether to automatically convert bare URLs into links (default: true)' do |auto_links|
61
69
  options[:auto_links] = auto_links
62
70
  end
@@ -1,5 +1,7 @@
1
1
  # encoding: UTF-8
2
2
  module Kramdown; module AsciiDoc
3
+ using CoreExt
4
+
3
5
  DEFAULT_PARSER_OPTS = {
4
6
  auto_ids: false,
5
7
  hard_wrap: false,
@@ -51,6 +53,7 @@ module Kramdown; module AsciiDoc
51
53
  ADMON_TYPE_MAP = ADMON_LABELS.map {|l, _| [l, l.upcase] }.to_h.merge 'Attention' => 'IMPORTANT', 'Hint' => 'TIP'
52
54
  BLOCK_TYPES = [:p, :blockquote, :codeblock, :table]
53
55
  DLIST_MARKERS = %w(:: ;; ::: ::::)
56
+ NON_DEFAULT_TABLE_ALIGNMENTS = [:center, :right]
54
57
  PUNCTUATION = %w(. ? !)
55
58
  # FIXME here we reverse the smart quotes; add option to allow them (needs to be handled carefully)
56
59
  SMART_QUOTE_ENTITY_TO_MARKUP = { ldquo: ?", rdquo: ?", lsquo: ?', rsquo: ?' }
@@ -74,18 +77,16 @@ module Kramdown; module AsciiDoc
74
77
  laquo_scape: '<< ',
75
78
  raquo_space: ' >>',
76
79
  }
77
- TABLE_ALIGNMENTS = {
78
- left: '<',
79
- center: '^',
80
- right: '>',
81
- }
82
-
83
- NON_DEFAULT_TABLE_ALIGNMENTS = [:center, :right]
80
+ TABLE_ALIGNMENTS = { left: '<', center: '^', right: '>' }
81
+ # NOTE assumes default Asciidoctor::Compliance.unique_id_start_index value
82
+ UNIQUE_ID_START_INDEX = 1
84
83
 
85
84
  CommentPrefixRx = /^ *! ?/m
86
85
  CssPropDelimRx = /\s*;\s*/
87
86
  FullStopRx = /(?<=\S\.|.\?|.!)\p{Blank}+/
88
87
  InadvertentReplacementsRx = /[-=]>|<[-=]|\.\.\.|\{\p{Word}[\p{Word}-]*\}/
88
+ InvalidIdCharsRx = /&(?:[a-z][a-z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-f][\da-f][\da-f]{0,3});|[^ \p{Word}\-.]+?/
89
+ ListMarkerRx = /^[ \t]*(?:(?:-|\*\*{0,4}|\.\.{0,4}|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|.*?(?::::{0,2}|;;)(?:$|[ \t]))/
89
90
  MenuRefRx = /^([\p{Word}&].*?)\s>\s([\p{Word}&].*(?:\s>\s|$))+/
90
91
  ReplaceableTextRx = /[-=]>|<[-=]| -- |\p{Word}--\p{Word}|\*\*|\.\.\.|&\S+;|\{\p{Word}[\p{Word}-]*\}|(?:https?|ftp):\/\/\p{Word}|\((?:C|R|TM)\)/
91
92
  SmartApostropheRx = /\b’\b/
@@ -104,6 +105,31 @@ module Kramdown; module AsciiDoc
104
105
  def initialize root, opts
105
106
  super
106
107
  @attributes = opts[:attributes] || {}
108
+ @auto_ids = opts[:auto_ids]
109
+ @lazy_ids = opts[:lazy_ids]
110
+ if @auto_ids || @lazy_ids
111
+ if @auto_ids
112
+ unless (@id_pre = opts[:auto_id_prefix]) == '_'
113
+ # NOTE only need to set idprefix when lazy_ids is set since otherwise all IDs are explicit
114
+ @attributes['idprefix'] = @id_pre if @lazy_ids
115
+ end
116
+ unless (sep = opts[:auto_id_separator] || '-') == '_'
117
+ # NOTE only need to set idseparator when lazy_ids is set since otherwise all IDs are explicit
118
+ @attributes['idseparator'] = sep if @lazy_ids
119
+ end
120
+ else
121
+ @id_pre = @attributes['idprefix'] || '_'
122
+ sep = @attributes['idseparator']
123
+ end
124
+ if sep
125
+ sep_replace = (sep = sep.chr) == '-' || sep == '.' ? ' .-' : %( #{sep}.-) unless sep.empty?
126
+ else
127
+ sep, sep_replace = '_', ' _.-'
128
+ end
129
+ @id_sep = sep
130
+ @id_sep_replace = sep_replace
131
+ end
132
+ @ids_seen = {}
107
133
  @auto_links = opts.fetch :auto_links, true
108
134
  @heading_offset = opts[:heading_offset] || 0
109
135
  @imagesdir = opts[:imagesdir] || @attributes['imagesdir']
@@ -132,6 +158,7 @@ module Kramdown; module AsciiDoc
132
158
  def convert_heading el, opts
133
159
  (writer = opts[:writer]).start_block
134
160
  level = el.options[:level] + @heading_offset
161
+ attrs = []
135
162
  style = []
136
163
  # Q: should writer track last heading level?
137
164
  if (discrete = @current_heading_level && level > @current_heading_level + 1)
@@ -145,23 +172,34 @@ module Kramdown; module AsciiDoc
145
172
  end
146
173
  if (child_i = to_element el.children[0]).type == :html_element && child_i.value == 'a' && (id = child_i.attr['id'])
147
174
  el = clone el, children: child_i.children + (el.children.drop 1)
148
- style << %(##{id})
175
+ unless @lazy_ids && id == (generate_unique_id (extract_raw_text el), false)
176
+ (id.include? '.') ? (attrs << %(id=#{id})) : (style << %(##{id}))
177
+ end
178
+ record_id id
149
179
  elsif (id = el.attr['id'])
150
- style << %(##{id})
180
+ # NOTE no need to check for '.' in this case since it's not recognized as a valid ID character by kramdown
181
+ style << %(##{id}) unless @lazy_ids && id == (generate_unique_id (extract_raw_text el), false)
182
+ record_id id
183
+ elsif @auto_ids
184
+ unless @lazy_ids
185
+ ((id = generate_unique_id (extract_raw_text el)).include? '.') ? (attrs << %(id=#{id})) : (style << %(##{id}))
186
+ end
151
187
  end
152
188
  if (role = el.attr['class'])
153
189
  style << %(.#{role.tr ' ', '.'})
154
190
  end
155
- lines = []
156
- lines << %([#{style.join}]) unless style.empty?
157
- # NOTE kramdown removes newlines from heading
158
- lines << %(#{'=' * level} #{compose_text el, strip: true})
191
+ attrs.unshift style.join unless style.empty?
192
+ attrlist = %([#{attrs.join ','}]) unless attrs.empty?
193
+ # NOTE kramdown has already removed newlines
194
+ title = compose_text el, strip: true
159
195
  if level == 1 && writer.empty? && @current_heading_level != 1
160
- writer.header.push(*lines)
196
+ writer.add_prologue_line attrlist if attrlist
197
+ writer.doctitle = title
161
198
  nil
162
199
  else
163
200
  @attributes['doctype'] = 'book' if level == 1
164
- writer.add_lines lines
201
+ writer.add_line attrlist if attrlist
202
+ writer.add_line %(#{'=' * level} #{title})
165
203
  end
166
204
  @current_heading_level = level unless discrete
167
205
  nil
@@ -241,12 +279,12 @@ module Kramdown; module AsciiDoc
241
279
  writer.add_line '----'
242
280
  writer.add_lines lines
243
281
  writer.add_line '----'
244
- elsif !prompt && (lines.include? '')
282
+ elsif !prompt && ((lines.include? '') || (ListMarkerRx.match? lines[0]))
245
283
  writer.add_line '....'
246
284
  writer.add_lines lines
247
285
  writer.add_line '....'
248
286
  else
249
- # NOTE clear the list continuation
287
+ # NOTE clear the list continuation as it isn't required
250
288
  writer.clear_line if writer.current_line == '+'
251
289
  writer.add_line lines.map {|l| %( #{l}) }
252
290
  end
@@ -254,14 +292,16 @@ module Kramdown; module AsciiDoc
254
292
 
255
293
  def convert_img el, opts
256
294
  if (parent = opts[:parent]).type == :p && parent.children.size == 1
295
+ attrs = []
257
296
  style = []
258
297
  if (id = el.attr['id'])
259
- style << %(##{id})
298
+ (id.include? '.') ? (attrs << %(id=#{id})) : (style << %(##{id}))
260
299
  end
261
300
  if (role = el.attr['class'])
262
301
  style << %(.#{role.tr ' ', '.'})
263
302
  end
264
- block_attributes_line = %([#{style.join}]) unless style.empty?
303
+ attrs.unshift style.join unless style.empty?
304
+ attrlist = %([#{attrs.join ','}]) unless attrs.empty?
265
305
  block = true
266
306
  end
267
307
  macro_attrs = [nil]
@@ -288,7 +328,7 @@ module Kramdown; module AsciiDoc
288
328
  writer = opts[:writer]
289
329
  if block
290
330
  writer.start_block
291
- writer.add_line block_attributes_line if block_attributes_line
331
+ writer.add_line attrlist if attrlist
292
332
  writer.add_line %(image::#{src}[#{macro_attrs.join ','}])
293
333
  else
294
334
  writer.append %(image:#{src}[#{macro_attrs.join ','}])
@@ -608,12 +648,14 @@ module Kramdown; module AsciiDoc
608
648
  end
609
649
  end
610
650
 
651
+ private
652
+
611
653
  def extract_prologue el, opts
612
654
  if (child_i = to_element (children = el.children)[0]).type == :xml_comment
613
655
  (prologue_el = el.dup).children = children.take_while {|child| child.type == :xml_comment || child.type == :blank }
614
656
  (el = el.dup).children = children.drop prologue_el.children.size
615
657
  traverse prologue_el, (opts.merge writer: (prologue_writer = Writer.new))
616
- opts[:writer].header.push(*prologue_writer.body)
658
+ opts[:writer].add_prologue_lines prologue_writer.body
617
659
  end
618
660
  el
619
661
  end
@@ -694,6 +736,47 @@ module Kramdown; module AsciiDoc
694
736
  result.join LF
695
737
  end
696
738
  end
739
+
740
+ def extract_raw_text node
741
+ node.children.reduce([]) {|accum, child| mine_text child, accum }.join
742
+ end
743
+
744
+ def mine_text node, accum
745
+ case node.type
746
+ when :text
747
+ accum << node.value
748
+ when :entity
749
+ accum << node.options[:original]
750
+ else
751
+ node.children.each {|child| mine_text child, accum }
752
+ end
753
+ accum
754
+ end
755
+
756
+ def generate_id str
757
+ id = %(#{pre = @id_pre}#{str.downcase.gsub InvalidIdCharsRx, ''})
758
+ if (sep_replace = @id_sep_replace)
759
+ id = id.tr_s sep_replace, (sep = @id_sep)
760
+ id = id.chop if id.end_with? sep
761
+ id = id.slice 1, id.length if pre.empty? && (id.start_with? sep)
762
+ else
763
+ id = id.delete ' '
764
+ end
765
+ id
766
+ end
767
+
768
+ def generate_unique_id str, record = true
769
+ id = generate_id str
770
+ if (seen_idx = @ids_seen[id])
771
+ id = %(#{id}#{@id_sep}#{seen_idx += 1})
772
+ end
773
+ record_id id, seen_idx if record
774
+ id
775
+ end
776
+
777
+ def record_id id, idx = nil
778
+ @ids_seen[id] = idx || UNIQUE_ID_START_INDEX
779
+ end
697
780
  end
698
781
  end; end
699
782