asciidoctor-html 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb08661857ac0a38bcb97d0f4eefc5d155f8f60ab720eb80d0ca5c0da82eab59
4
- data.tar.gz: bb88cc92bcfc63edbc90bca406cad9818a577a13ea4722740364b96c5843fa70
3
+ metadata.gz: 2dfaf44dfed22e54fb5cac254f5dd1f18618cb40167bb2a506d2ba37ef381292
4
+ data.tar.gz: 5c9915bf539614fbcfdb8578009db076324c87e172a0871f4ee9383c68aed4f3
5
5
  SHA512:
6
- metadata.gz: e1e5f386607dea80a054782df8e20a7e5d2f35d1b5c94b0504a4df50d8c66f61d652d3555e1d2b5ddaecccf44a545adf235995426bf644d76677aa63136d016d
7
- data.tar.gz: 055f53673e99b88002a51381aa2b4730c3664d8d74cf9caadcf0fedfc3d3e23d013dc8a8fa5b2a66a25fcaf79e262b86fd1d8d727a4dd40ea908d6248981816b
6
+ metadata.gz: fe41726063320cc979c2ed2e117726ac8543dfde3337fd6fa9600a671c37bdcb25891b431147db21cf6d22121e9531f7d9fead0ddc246c430cce754acbbd52fd
7
+ data.tar.gz: b3c63eb1f916812247b58f9f397dd316149e2625e49fe23869137a7eb920cba0f0a34e966102984168f13695940b36657a4b41354069c62e176dda0c7358a0e9
data/.rubocop.yml CHANGED
@@ -2,6 +2,24 @@ AllCops:
2
2
  TargetRubyVersion: 3.1
3
3
  NewCops: enable
4
4
 
5
+ Metrics/AbcSize:
6
+ Max: 40
7
+
8
+ Metrics/CyclomaticComplexity:
9
+ Max: 15
10
+
11
+ Metrics/PerceivedComplexity:
12
+ Max: 20
13
+
14
+ Metrics/BlockNesting:
15
+ Max: 5
16
+
17
+ Metrics/MethodLength:
18
+ Max: 30
19
+
20
+ Metrics/ClassLength:
21
+ Max: 200
22
+
5
23
  Style/StringLiterals:
6
24
  EnforcedStyle: double_quotes
7
25
 
data/CHANGELOG.md CHANGED
@@ -3,3 +3,9 @@
3
3
  ## [0.1.0] - 2025-06-09
4
4
 
5
5
  - Initial release
6
+
7
+ ## [0.1.1] - 2025-06-29
8
+
9
+ - Add custom olist conversion.
10
+ - Automatic processing of reftexts based on block numbering scheme.
11
+ - Automatic processing of anchor reftexts based on list item labels.
data/README.md CHANGED
@@ -1,21 +1,23 @@
1
+ [![Build](https://github.com/ravirajani/asciidoctor-html/actions/workflows/main.yml/badge.svg)](https://github.com/ravirajani/asciidoctor-html/actions/workflows/main.yml)
2
+
1
3
  # Asciidoctor::Html
2
4
 
3
- This gem provides an alternative HTML converter for [Asciidoctor](https://github.com/asciidoctor/asciidoctor).
5
+ **The code in this repo is being actively developed and currently has limited functionality.**
4
6
 
5
- ## Installation
7
+ When complete, this gem will provide an alternative HTML converter for [Asciidoctor](https://github.com/asciidoctor/asciidoctor) as well as Jekyll-based book generator.
6
8
 
7
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
9
+ ## Installation
8
10
 
9
11
  Install the gem and add to the application's Gemfile by executing:
10
12
 
11
13
  ```bash
12
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
14
+ bundle add asciidoctor-html
13
15
  ```
14
16
 
15
17
  If bundler is not being used to manage dependencies, install the gem by executing:
16
18
 
17
19
  ```bash
18
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
20
+ gem install asciidoctor-html
19
21
  ```
20
22
 
21
23
  ## Usage
@@ -33,14 +35,7 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
33
35
 
34
36
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
35
37
 
36
- ## Contributing
37
-
38
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/asciidoctor-html. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/asciidoctor-html/blob/main/CODE_OF_CONDUCT.md).
39
38
 
40
39
  ## License
41
40
 
42
41
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
43
-
44
- ## Code of Conduct
45
-
46
- Everyone interacting in the Asciidoctor::Html project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/asciidoctor-html/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
+ require "jekyll"
4
5
  require "minitest/test_task"
5
6
 
6
7
  Minitest::TestTask.create
@@ -9,4 +10,13 @@ require "rubocop/rake_task"
9
10
 
10
11
  RuboCop::RakeTask.new
11
12
 
13
+ task build: %i[test rubocop] do
14
+ config = Jekyll.configuration({
15
+ source: "./docs",
16
+ destination: "./_site"
17
+ })
18
+ site = Jekyll::Site.new(config)
19
+ Jekyll::Commands::Build.build site, config
20
+ end
21
+
12
22
  task default: %i[test rubocop]
data/docs/_config.yml ADDED
@@ -0,0 +1,5 @@
1
+ title: asciidoctor-html docs
2
+
3
+ exclude:
4
+ - package.json
5
+ - package-lock.json
@@ -0,0 +1,25 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>{{ page.title }} | {{ site.title }}</title>
7
+ <link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
8
+ <link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
9
+ <link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
10
+ <link rel="manifest" href="site.webmanifest">
11
+ <link rel="stylesheet" href="assets/css/styles.css">
12
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/default.min.css">
13
+ <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
14
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
15
+ </head>
16
+ <body>
17
+ <main class="content-container">
18
+ <h1 class="display-4">{{ page.title }}</h1>
19
+ {{ content }}
20
+ </main>
21
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js" integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO" crossorigin="anonymous"></script>
22
+ <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/languages/asciidoc.min.js"></script>
23
+ <script>hljs.highlightAll();</script>
24
+ </body>
25
+ </html>
@@ -0,0 +1,35 @@
1
+ $content-padding-x: 0.75rem;
2
+ $content-padding-y: 0.5rem;
3
+ $content-width: 90rch;
4
+
5
+ main {
6
+ margin: $spacer auto;
7
+ }
8
+
9
+ .full-width-bg {
10
+ padding-left: $content-padding-x;
11
+ padding-right: $content-padding-x;
12
+ margin-left: -$content-padding-x;
13
+ margin-right: -$content-padding-x;
14
+ }
15
+
16
+ .content-container {
17
+ max-width: $content-width;
18
+ padding-left: $content-padding-x;
19
+ padding-right: $content-padding-x;
20
+ }
21
+
22
+ .title-mark {
23
+ margin-right: 1.5ex;
24
+ }
25
+
26
+ .title-prefix {
27
+ margin-right: 1ex;
28
+ &::after {
29
+ content: ':';
30
+ }
31
+ }
32
+
33
+ .block-title {
34
+ margin-bottom: 0.25rem;
35
+ }
@@ -0,0 +1,30 @@
1
+ .example {
2
+ @extend .full-width-bg;
3
+ padding-top: $content-padding-y;
4
+ padding-bottom: $content-padding-y;
5
+ margin-bottom: $spacer;
6
+ border-top: 2px solid var(--example-border-color, var(--bs-secondary-border-subtle));
7
+ border-bottom: 2px solid var(--example-border-color, var(--bs-secondary-border-subtle));
8
+ background-color: var(--example-bg-color, var(--bs-secondary-bg-subtle));
9
+ > *:last-child {
10
+ margin-bottom: 0;
11
+ }
12
+ }
13
+
14
+ $thm-types: theorem, proposition, lemma, corollary;
15
+
16
+ @each $type in $thm-types {
17
+ .example-#{$type} {
18
+ --example-border-color: var(--bs-primary-border-subtle);
19
+ --example-bg-color: var(--bs-primary-bg-subtle);
20
+ }
21
+ }
22
+
23
+ .example-algorithm {
24
+ --example-border-color: #{tint-color($orange, 60%)};
25
+ --example-bg-color: #{tint-color($orange, 80%)};
26
+ @include color-mode(dark) {
27
+ --example-border-color: #{shade-color($orange, 40%)};
28
+ --example-bg-color: #{shade-color($orange, 80%)};
29
+ }
30
+ }
@@ -0,0 +1,17 @@
1
+ .figbox {
2
+ margin: 1.5rem -#{$content-padding-x};
3
+ }
4
+ figure {
5
+ text-align: center;
6
+ img {
7
+ @extend .figure-img;
8
+ @extend .img-fluid;
9
+ max-height: 400px;
10
+ }
11
+ figcaption {
12
+ padding-left: $content-padding-x;
13
+ padding-right: $content-padding-x;
14
+ text-align: center;
15
+ @extend .figure-caption;
16
+ }
17
+ }
@@ -0,0 +1,101 @@
1
+ .arabic, .lowerroman {
2
+ > li .li-mark::after {
3
+ content: '.';
4
+ }
5
+ }
6
+ .loweralpha, .upperalpha {
7
+ > li .li-mark {
8
+ &::before { content: '('; }
9
+ &::after { content: ')'; }
10
+ }
11
+ }
12
+
13
+ .list-wrapper {
14
+ margin-bottom: $spacer;
15
+ ol, ul {
16
+ margin-bottom: 0;
17
+ }
18
+ }
19
+
20
+ .olist {
21
+ @extend .list-unstyled;
22
+ li {
23
+ display: flex;
24
+ flex-flow: row nowrap;
25
+ gap: 0.5rem;
26
+ align-items: first baseline;
27
+ .li-mark {
28
+ flex: 0 0 1.5rem;
29
+ text-align: right;
30
+ }
31
+ .li-content {
32
+ flex: 1;
33
+ p { margin-bottom: 0.25rem; }
34
+ }
35
+ }
36
+ &.pseudocode {
37
+ @mixin pcode-style($initial-gap, $indent) {
38
+ $mark-width: 1.25rem;
39
+ > li {
40
+ gap: $initial-gap;
41
+ > .li-mark {
42
+ flex: 0 0 $mark-width;
43
+ }
44
+ }
45
+ @for $i from 1 through 3 {
46
+ &.level-#{$i + 1} {
47
+ margin-left: -($i - 1)*$indent - $initial-gap - $mark-width;
48
+ > li {
49
+ gap: $i*$indent + $initial-gap;
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ @include pcode-style($initial-gap: 0.75rem, $indent: 1.5rem);
56
+
57
+ @include media-breakpoint-up(sm) {
58
+ @include pcode-style($initial-gap: 0.75rem, $indent: 1.75rem);
59
+ }
60
+
61
+ @include media-breakpoint-up(md) {
62
+ @include pcode-style($initial-gap: 1rem, $indent: 2rem);
63
+ }
64
+
65
+ .li-mark {
66
+ font-size: 90%;
67
+ color: var(--bs-tertiary-color);
68
+ }
69
+ }
70
+ }
71
+
72
+ .figlist-wrapper {
73
+ @extend .figbox;
74
+ .figlist {
75
+ margin-bottom: 0;
76
+ @extend .list-unstyled;
77
+ display: flex;
78
+ flex-flow: row wrap;
79
+ gap: 0.75rem;
80
+ justify-content: space-evenly;
81
+ align-items: last baseline;
82
+ li {
83
+ flex: 1;
84
+ .li-mark {
85
+ margin-right: 1ex;
86
+ }
87
+ figure {
88
+ margin-bottom: 0.25rem;
89
+ img {
90
+ min-width: 300px;
91
+ }
92
+ }
93
+ }
94
+ }
95
+ .block-title {
96
+ padding-left: $content-padding-x;
97
+ padding-right: $content-padding-x;
98
+ text-align: center;
99
+ @extend .figure-caption;
100
+ }
101
+ }
@@ -0,0 +1,40 @@
1
+ // main.scss
2
+
3
+ // 1. Include functions first (so you can manipulate colors, SVGs, calc, etc)
4
+ @import "../node_modules/bootstrap/scss/functions";
5
+
6
+ // 2. Include any default variable overrides here
7
+ $color-mode-type: media-query;
8
+
9
+ // 3. Include remainder of required Bootstrap stylesheets (including any separate color mode stylesheets)
10
+ @import "../node_modules/bootstrap/scss/variables";
11
+ @import "../node_modules/bootstrap/scss/variables-dark";
12
+
13
+ // 4. Include any default map overrides here
14
+
15
+ // 5. Include remainder of required parts
16
+ @import "../node_modules/bootstrap/scss/maps";
17
+ @import "../node_modules/bootstrap/scss/mixins";
18
+ @import "../node_modules/bootstrap/scss/root";
19
+
20
+ // 6. Include any other optional stylesheet partials as desired; list below is not inclusive of all available stylesheets
21
+ @import "../node_modules/bootstrap/scss/utilities";
22
+ @import "../node_modules/bootstrap/scss/reboot";
23
+ @import "../node_modules/bootstrap/scss/type";
24
+ @import "../node_modules/bootstrap/scss/images";
25
+ @import "../node_modules/bootstrap/scss/containers";
26
+ @import "../node_modules/bootstrap/scss/grid";
27
+ @import "../node_modules/bootstrap/scss/helpers";
28
+ @import "../node_modules/bootstrap/scss/buttons";
29
+ @import "../node_modules/bootstrap/scss/transitions";
30
+ // ...
31
+
32
+ // 7. Optionally include utilities API last to generate classes based on the Sass map in `_utilities.scss`
33
+ @import "../node_modules/bootstrap/scss/utilities/api";
34
+
35
+ // 8. Add additional custom code here
36
+ @import "../node_modules/bootstrap-icons/font/bootstrap-icons";
37
+ @import "custom";
38
+ @import "example";
39
+ @import "olist";
40
+ @import "figure";
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ ../../node_modules/bootstrap-icons/font/fonts
@@ -0,0 +1,3 @@
1
+ ---
2
+ ---
3
+ @import "main";
Binary file
Binary file
Binary file
Binary file
Binary file
data/docs/favicon.ico ADDED
Binary file
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "docs",
3
+ "lockfileVersion": 3,
4
+ "requires": true,
5
+ "packages": {
6
+ "": {
7
+ "dependencies": {
8
+ "bootstrap": "^5.3.6",
9
+ "bootstrap-icons": "^1.13.1"
10
+ }
11
+ },
12
+ "node_modules/@popperjs/core": {
13
+ "version": "2.11.8",
14
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
15
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
16
+ "license": "MIT",
17
+ "peer": true,
18
+ "funding": {
19
+ "type": "opencollective",
20
+ "url": "https://opencollective.com/popperjs"
21
+ }
22
+ },
23
+ "node_modules/bootstrap": {
24
+ "version": "5.3.6",
25
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
26
+ "integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
27
+ "funding": [
28
+ {
29
+ "type": "github",
30
+ "url": "https://github.com/sponsors/twbs"
31
+ },
32
+ {
33
+ "type": "opencollective",
34
+ "url": "https://opencollective.com/bootstrap"
35
+ }
36
+ ],
37
+ "license": "MIT",
38
+ "peerDependencies": {
39
+ "@popperjs/core": "^2.11.8"
40
+ }
41
+ },
42
+ "node_modules/bootstrap-icons": {
43
+ "version": "1.13.1",
44
+ "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz",
45
+ "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==",
46
+ "funding": [
47
+ {
48
+ "type": "github",
49
+ "url": "https://github.com/sponsors/twbs"
50
+ },
51
+ {
52
+ "type": "opencollective",
53
+ "url": "https://opencollective.com/bootstrap"
54
+ }
55
+ ],
56
+ "license": "MIT"
57
+ }
58
+ }
59
+ }
data/docs/package.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "dependencies": {
3
+ "bootstrap": "^5.3.6",
4
+ "bootstrap-icons": "^1.13.1"
5
+ }
6
+ }
@@ -0,0 +1 @@
1
+ {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "asciidoctor"
4
+ require_relative "olist"
4
5
  require_relative "utils"
6
+ require_relative "figure"
5
7
 
6
8
  module Asciidoctor
7
9
  module Html
@@ -9,26 +11,61 @@ module Asciidoctor
9
11
  class Converter < (Asciidoctor::Converter.for "html5")
10
12
  register_for "html5"
11
13
 
14
+ include Figure
15
+
12
16
  def convert_section(node)
13
- doc_attrs = node.document.attributes
17
+ document = node.document
14
18
  level = node.level
15
- show_sectnum = node.numbered && level <= (doc_attrs["sectnumlevels"] || 3).to_i
16
- tag_name = %(h#{level + 1})
17
- sectnum = show_sectnum ? %(<span class="sec-mark">#{node.sectnum ""}</span>) : ""
18
- content = %(<#{tag_name}#{Utils.id_class_attr_str nil, node.role}>) +
19
- %(#{sectnum}#{node.title}</#{tag_name}>\n\n#{node.content})
19
+ show_sectnum = node.numbered && level <= (document.attr("sectnumlevels") || 1).to_i
20
+ tag_name = %(h#{[level + 2, 6].min})
21
+ sectnum = show_sectnum ? %(<span class="title-mark">#{node.sectnum ""}</span>) : ""
22
+ content = %(<#{tag_name}>#{sectnum}#{node.title}) +
23
+ %(</#{tag_name}>\n#{node.content})
20
24
  Utils.wrap_node content, node, :section
21
25
  end
22
26
 
23
27
  def convert_paragraph(node)
24
- content = %(#{Utils.display_title node}<p>#{node.content}</p>)
25
- node.title? ? Utils.wrap_node(content, node) : "#{content}\n"
28
+ content = %(<p>#{node.content}</p>\n)
29
+ Utils.wrap_node_with_title content, node
26
30
  end
27
31
 
28
32
  def convert_example(node)
29
- node.set_attr "reftext", Utils.title_prefix(node)
30
- content = Utils.display_title(node) + node.content
31
- Utils.wrap_node content, node
33
+ p node.context unless Utils.show_title?(node)
34
+ Utils.wrap_node_with_title node.content, node, needs_prefix: true
35
+ end
36
+
37
+ def convert_image(node)
38
+ return super if node.option?("inline") || node.option?("interactive")
39
+
40
+ content = display_figure node
41
+ Utils.wrap_id_classes content, node.id, ["figbox", node.role].compact.join(" ")
42
+ end
43
+
44
+ def convert_inline_image(node)
45
+ return super if node.option?("inline") || node.option?("interactive")
46
+
47
+ target = node.target
48
+ mark = node.parent.attr("mark")
49
+ attrs = image_attrs node
50
+ image = display_image node, target, attrs
51
+ title = node.attr?("title") ? node.attr("title") : ""
52
+ caption = mark ? %(<span class="li-mark">#{mark}</span>#{title}) : title
53
+ %( #{image}\n <figcaption>#{caption}</figcaption>)
54
+ end
55
+
56
+ def convert_olist(node)
57
+ return convert_figlist(node) if node.style == "figlist"
58
+
59
+ depth = node.attr "list-depth"
60
+ flat = node.attr? "flat-style"
61
+ level = depth + 1
62
+ classes = ["olist level-#{level}", flat ? "pseudocode" : node.style, node.role].compact.join(" ")
63
+ result = [%(<ol#{Utils.dyn_id_class_attr_str node, classes}>)]
64
+ node.items.each do |item|
65
+ result << Olist.display_list_item(item)
66
+ end
67
+ result << %(</ol> <!-- .level-#{level} -->\n)
68
+ Utils.wrap_id_classes_with_title result.join("\n"), node, node.id, "list-wrapper"
32
69
  end
33
70
  end
34
71
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "olist"
4
+
5
+ module Asciidoctor
6
+ module Html
7
+ # Helper functions for the image/figure conversion.
8
+ # Mixed into the Converter class.
9
+ module Figure
10
+ def display_image(node, target, attrs)
11
+ %(<img src="#{node.image_uri target}" #{attrs}#{@void_element_slash}>)
12
+ end
13
+
14
+ def image_attrs(node)
15
+ width = node.attr?("width") ? %( width="#{node.attr "width"}") : ""
16
+ height = node.attr?("height") ? %( height="#{node.attr "height"}") : ""
17
+ alt = encode_attribute_value node.alt
18
+ %(alt="#{alt}"#{width}#{height})
19
+ end
20
+
21
+ def display_figure(node)
22
+ target = node.attr "target"
23
+ title = node.title? ? node.title : ""
24
+ attrs = image_attrs node
25
+ image = display_image node, target, attrs
26
+ caption = %(<figcaption>#{Utils.display_title_prefix node}#{title}</figcaption>)
27
+ %(<figure>\n #{image}\n #{caption}\n</figure>)
28
+ end
29
+
30
+ def convert_figlist(node)
31
+ result = node.items.map do |item|
32
+ %(<li#{Utils.id_class_attr_str item.id}><figure>\n#{item.text}\n</figure></li>)
33
+ end
34
+ content = Utils.wrap_id_classes result.join("\n"), nil, "figlist loweralpha", :ol
35
+ title = Utils.display_title node
36
+ classes = ["figlist-wrapper", node.role].compact.join(" ")
37
+ Utils.wrap_id_classes %(#{content}#{title}), node.id, classes
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Html
5
+ # Helper functions for the olist conversion.
6
+ module Olist
7
+ def self.display_list_item(item)
8
+ result = []
9
+ result << %(<li#{Utils.id_class_attr_str item.id,
10
+ item.role}><div class="li-mark">#{item.attr "mark"}</div>)
11
+ result << %(<div class="li-content"><p>#{item.text}</p>)
12
+ result << "\n#{item.content}" if item.blocks?
13
+ result << %(</div></li>#{Utils.id_class_sel_comment item.id, item.role})
14
+ result.join "\n"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roman-numerals"
4
+ require_relative "tree_walker"
5
+
6
+ module Asciidoctor
7
+ module Html
8
+ # Traverses the document tree and attaches a correct reftext to
9
+ # numbered nodes.
10
+ class RefTreeProcessor < Asciidoctor::Extensions::TreeProcessor
11
+ NUMBERED_CONTEXTS = {
12
+ example: "thm-number",
13
+ table: "tbl-number",
14
+ image: "fig-number"
15
+ }.freeze
16
+
17
+ def number_within(document)
18
+ return :chapter if document.attr? "chapnum"
19
+ return :section if document.attr? "sectnums"
20
+
21
+ :document
22
+ end
23
+
24
+ def assign_numeral!(node, document, counter_name)
25
+ document.counters[counter_name] ||= 0
26
+ node.numeral = (document.counters[counter_name] += 1)
27
+ end
28
+
29
+ def relative_numeral(node, document, sectnum)
30
+ if node.numeral
31
+ prefix_number = (document.attr("chapnum") || sectnum).to_i
32
+ prefix_number.positive? ? "#{prefix_number}.#{node.numeral}" : node.numeral.to_s
33
+ else
34
+ ""
35
+ end
36
+ end
37
+
38
+ def process_numbered_block!(block, document, sectnum)
39
+ context = block.context
40
+ env = (block.style || context).to_s
41
+ env = "figure" if context == :image || env == "figlist"
42
+ block.set_attr "showcaption", true
43
+ assign_numeral! block, document, NUMBERED_CONTEXTS[context]
44
+ title_prefix = "#{env.capitalize} #{relative_numeral block, document, sectnum}"
45
+ block.set_attr "reftext", title_prefix
46
+ end
47
+
48
+ def process_numbered_block?(block)
49
+ NUMBERED_CONTEXTS.key?(block.context) || block.style == "figlist"
50
+ end
51
+
52
+ def li_mark(depth, idx)
53
+ case depth
54
+ when 0
55
+ idx + 1
56
+ when 1
57
+ ("a".."z").to_a[idx]
58
+ when 2
59
+ RomanNumerals.to_roman(idx + 1).downcase
60
+ when 3
61
+ ("a".."z").to_a[idx].upcase
62
+ end
63
+ end
64
+
65
+ def ref_li_mark(mark, depth)
66
+ return mark.to_s if depth.even?
67
+
68
+ "(#{mark})"
69
+ end
70
+
71
+ def offset(list)
72
+ list.attr?("start") ? (list.attr("start").to_i - 1) : 0
73
+ end
74
+
75
+ def register_reftext!(document, anchor_id, reftext)
76
+ node = document.catalog[:refs][anchor_id]
77
+ node&.text = reftext
78
+ end
79
+
80
+ def process_olist!(block, depth, flat_style: false)
81
+ parent_reftext = ""
82
+ document = block.document
83
+ if depth.positive?
84
+ parent = block.parent
85
+ parent = parent.parent until parent.context == :list_item
86
+ parent_reftext = parent.reftext? ? parent.reftext : ""
87
+ end
88
+ block.set_attr "list-depth", depth
89
+ if flat_style
90
+ block.set_attr("flat-style", true)
91
+ else
92
+ offset = offset block
93
+ block.items.each_with_index do |item, idx|
94
+ d = block.style == "figlist" ? 1 : depth
95
+ mark = li_mark(d, idx + offset)
96
+ item.set_attr "mark", mark
97
+ item_reftext = "#{parent_reftext}#{ref_li_mark mark, d}"
98
+ item.set_attr "reftext", item_reftext
99
+ /^<a id="(?<id>.+?)"/ =~ item.text
100
+ register_reftext! document, id, item_reftext if id
101
+ end
102
+ end
103
+ end
104
+
105
+ def process_flat_item!(item, idx)
106
+ mark = li_mark(0, idx)
107
+ item.set_attr "mark", mark
108
+ item.set_attr "reftext", ref_li_mark(mark, 0)
109
+ end
110
+
111
+ def reset_counters!(document)
112
+ counters = document.counters
113
+ NUMBERED_CONTEXTS.each_value do |counter_name|
114
+ counters[counter_name] = 0
115
+ end
116
+ end
117
+
118
+ def process(document)
119
+ sectnum = 0
120
+ listdepth = 0
121
+ flat_style = false
122
+ flat_idx = 0 # flat index for (pseudocode) list
123
+ tw = TreeWalker.new(document)
124
+ while (block = tw.next_block)
125
+ context = block.context
126
+ unless block.attr? "refprocessed"
127
+ process_numbered_block!(block, document, sectnum) if process_numbered_block?(block)
128
+ if context == :section && block.level == 1 && number_within(document) == :section
129
+ sectnum += 1
130
+ reset_counters! document
131
+ elsif context == :olist
132
+ if listdepth.zero?
133
+ flat_style = (block.style == "pseudocode")
134
+ flat_idx = offset block
135
+ end
136
+ process_olist!(block, listdepth, flat_style:)
137
+ elsif context == :list_item && flat_style
138
+ process_flat_item!(block, flat_idx)
139
+ flat_idx += 1
140
+ end
141
+ block.set_attr "refprocessed", true
142
+ end
143
+ tw.walk do |move|
144
+ listdepth += 1 if context == :olist && move == :explore
145
+ listdepth -= 1 if context == :list_item && move == :retreat
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Html
5
+ # Walks the document tree
6
+ class TreeWalker
7
+ def initialize(document, max_levels = 20)
8
+ @max_levels = max_levels
9
+ @idx = [0] * (max_levels + 1) # index of next unexplored block at each level
10
+ @path = [document]
11
+ @level = 0 # the current level
12
+ end
13
+
14
+ def next_block
15
+ return nil if @path.empty?
16
+
17
+ @path.last
18
+ end
19
+
20
+ def walk(&callback)
21
+ block = next_block
22
+ return nil unless block
23
+
24
+ if block.blocks? && @level < @max_levels && @idx[@level + 1] < block.blocks.size
25
+ @level += 1
26
+ @path.push(block.blocks[@idx[@level]])
27
+ callback.call(:explore)
28
+ else
29
+ @idx[@level + 1] = 0 if @level < @max_levels
30
+ @idx[@level] += 1
31
+ @level -= 1
32
+ @path.pop
33
+ callback.call(:retreat)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -10,45 +10,61 @@ module Asciidoctor
10
10
  "#{id_attr}#{class_attr}"
11
11
  end
12
12
 
13
+ # Don't include an id if the element will be wrapped
14
+ # by a title, since the wrapper should have the id.
15
+ def self.dyn_id_class_attr_str(node, classes = nil)
16
+ id = node.title? ? nil : node.id
17
+ id_class_attr_str id, classes
18
+ end
19
+
13
20
  def self.id_class_sel_str(id, classes)
14
21
  result = ""
15
22
  result += "##{id}" if id
16
23
  result + ".#{classes.tr "\s", "."}" if classes
17
24
  end
18
25
 
19
- def self.display_number(node)
20
- if node.numeral
21
- chapter_number = node.document.attr("chapnum")
22
- chapter_number ? "#{chapter_number}.#{node.numeral}" : node.numeral.to_s
23
- else
24
- ""
25
- end
26
+ def self.id_class_sel_comment(id, classes)
27
+ id || (classes && !classes.empty?) ? " <!-- #{id_class_sel_str id, classes} -->" : ""
26
28
  end
27
29
 
28
- def self.display_title(node)
29
- prefix = display_title_prefix node
30
- title = prefix.empty? ? node.title : %(<span class="title-content">#{node.title}</span>)
31
- node.title? ? %(<h5 class="block-title">#{prefix}#{title}</h5>\n) : ""
30
+ def self.show_title?(node)
31
+ node.attr?("showcaption") || node.title?
32
32
  end
33
33
 
34
- def self.title_prefix(node)
35
- (node.style ? "#{node.style.capitalize} " : "") + display_number(node)
34
+ def self.display_title(node, needs_prefix: true)
35
+ prefix = needs_prefix ? display_title_prefix(node) : ""
36
+ show_title?(node) ? %(<h6 class="block-title">#{prefix}#{node.title}</h6>\n) : ""
36
37
  end
37
38
 
38
39
  def self.display_title_prefix(node)
39
- prefix = title_prefix node
40
- prefix.empty? ? "" : %(<span class="title-prefix">#{prefix}</span>)
40
+ prefix = node.reftext? ? node.reftext : ""
41
+ node.title? && !node.title.empty? ? %(<span class="title-prefix">#{prefix}</span>) : prefix
41
42
  end
42
43
 
43
44
  def self.wrap_id_classes(content, id, classes, tag_name = :div)
44
45
  id_class = id_class_attr_str id, classes
45
- %(<#{tag_name}#{id_class}>\n#{content}\n</#{tag_name}> <!-- #{id_class_sel_str id, classes} -->\n)
46
+ %(<#{tag_name}#{id_class}>\n#{content}\n</#{tag_name}>#{id_class_sel_comment id, classes}\n)
46
47
  end
47
48
 
48
49
  def self.wrap_node(content, node, tag_name = :div)
49
- classes = [node.context, node.style, node.role].compact.join " "
50
+ base_class = node.context
51
+ mod = node.attr?("env") ? node.attr("env") : node.style
52
+ mod_class = if mod && mod != base_class.to_s
53
+ "#{base_class}-#{mod}"
54
+ else
55
+ ""
56
+ end
57
+ classes = [base_class, mod_class, node.role].compact.map(&:to_s).uniq.join(" ").strip
50
58
  wrap_id_classes content, node.id, classes, tag_name
51
59
  end
60
+
61
+ def self.wrap_node_with_title(content, node, tag_name = :div, needs_prefix: false)
62
+ show_title?(node) ? wrap_node(display_title(node, needs_prefix:) + content, node, tag_name) : content
63
+ end
64
+
65
+ def self.wrap_id_classes_with_title(content, node, id, classes, needs_prefix: false)
66
+ show_title?(node) ? wrap_id_classes(display_title(node, needs_prefix:) + content, id, classes) : content
67
+ end
52
68
  end
53
69
  end
54
70
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module Html
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.1"
6
6
  end
7
7
  end
@@ -2,3 +2,4 @@
2
2
 
3
3
  require_relative "html/version"
4
4
  require_relative "html/converter"
5
+ require_relative "html/ref_tree_processor"
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "cgi"
5
+
6
+ # Add class to Minitest module
7
+ module Minitest
8
+ # Custom reporter class that creates an HTML file in the docs folder
9
+ class HTMLReporter < AbstractReporter
10
+ DOCS_DIR = "#{__dir__}/../../docs".freeze
11
+ TESTS_DIR = "#{__dir__}/../../test/asciidoctor/html".freeze
12
+
13
+ def initialize
14
+ @results = {}
15
+ super
16
+ end
17
+
18
+ def record(result)
19
+ @results[result.name] = result.failures
20
+ end
21
+
22
+ def display_failure(failure, color)
23
+ %(<pre class="border border-#{color}"><code class="language-shell">#{failure}</code></pre>\n)
24
+ end
25
+
26
+ def display_result_title(name, id, failed, color)
27
+ style = %(style="vertical-align: -0.125em;")
28
+ status_icon = %(<i class="bi bi-#{failed ? "x" : "check"}-square text-#{color}" #{style}></i>)
29
+ chevron = %(<i class="bi bi-chevron-expand"></i>)
30
+ attrs = %(type="button" data-bs-toggle="collapse" data-bs-target="##{id}")
31
+ %(#{status_icon}<button #{attrs} class="btn btn-link">#{chevron} #{name.tr("_", " ").capitalize}</button>\n)
32
+ end
33
+
34
+ def display_result(name, adoc, html)
35
+ key = "test_#{name}"
36
+ failed = @results[key]&.size&.positive?
37
+ color = failed ? "danger" : "success"
38
+ id = "test-#{name.tr "_", "-"}"
39
+ title = display_result_title name, id, failed, color
40
+ pre = %(<pre><code class="language-asciidoc">#{CGI.escapeHTML adoc}</code></pre>\n)
41
+ fail = failed ? display_failure(CGI.escapeHTML(@results[key].join("\n")), color) : ""
42
+ %(<div>#{title}<div class="collapse full-width-bg" id="#{id}">#{pre}#{fail}#{html}</div></div>)
43
+ end
44
+
45
+ def report_files(results, dirname)
46
+ dirname.children.sort.each do |filepath|
47
+ next unless filepath.extname == ".adoc"
48
+
49
+ results << display_result(filepath.basename.sub_ext("").to_s,
50
+ File.read(filepath), File.read(filepath.sub_ext(".html")))
51
+ end
52
+ end
53
+
54
+ def report
55
+ frontmatter = %(---\nlayout: default\ntitle: Test Results\n---\n)
56
+ time = %(<p class="lead">#{Time.now.strftime("%d/%m/%Y %H:%M")}</p>\n)
57
+ results = []
58
+ Pathname(TESTS_DIR).children.sort.each do |pn|
59
+ next unless pn.directory?
60
+
61
+ report_files results, pn
62
+ end
63
+ html = %(#{frontmatter}#{time}#{results.join "\n"})
64
+ File.write("#{DOCS_DIR}/index.html", html)
65
+ end
66
+ end
67
+
68
+ def self.plugin_html_init(_options)
69
+ reporter << HTMLReporter.new
70
+ end
71
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciidoctor-html
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ravi Rajani
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: roman-numerals
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.3'
26
40
  email:
27
41
  - ravi.inajar@gmail.com
28
42
  executables: []
@@ -35,10 +49,36 @@ files:
35
49
  - LICENSE.txt
36
50
  - README.md
37
51
  - Rakefile
52
+ - docs/_config.yml
53
+ - docs/_layouts/default.html
54
+ - docs/_sass/_custom.scss
55
+ - docs/_sass/_example.scss
56
+ - docs/_sass/_figure.scss
57
+ - docs/_sass/_olist.scss
58
+ - docs/_sass/main.scss
59
+ - docs/android-chrome-192x192.png
60
+ - docs/android-chrome-512x512.png
61
+ - docs/apple-touch-icon.png
62
+ - docs/assets/css/fonts
63
+ - docs/assets/css/styles.scss
64
+ - docs/assets/img/cat1.jpg
65
+ - docs/assets/img/cat2.jpg
66
+ - docs/assets/img/cat3.jpg
67
+ - docs/favicon-16x16.png
68
+ - docs/favicon-32x32.png
69
+ - docs/favicon.ico
70
+ - docs/package-lock.json
71
+ - docs/package.json
72
+ - docs/site.webmanifest
38
73
  - lib/asciidoctor/html.rb
39
74
  - lib/asciidoctor/html/converter.rb
75
+ - lib/asciidoctor/html/figure.rb
76
+ - lib/asciidoctor/html/olist.rb
77
+ - lib/asciidoctor/html/ref_tree_processor.rb
78
+ - lib/asciidoctor/html/tree_walker.rb
40
79
  - lib/asciidoctor/html/utils.rb
41
80
  - lib/asciidoctor/html/version.rb
81
+ - lib/minitest/html_plugin.rb
42
82
  homepage: https://github.com/ravirajani/asciidoctor-html
43
83
  licenses:
44
84
  - MIT