Skip to content

Commit 955a200

Browse files
authored
Improve direct html generation (#1425)
This improves our plugin for direct html generation in Asciidoctor to the point where the diff for the perl client's docs is empty. Now, that isn't as big a feat as it sounds because: 1. Most of the perl client's docs are on CPAM so the docs we actually build are pretty small. 2. I pull a few tricks with `html_diff` to ignore a few changes that don't *look* like they make any visual difference to me. Even with those two caveates, this is pretty good progress. It switches the perl client to direct html, because, well, I did all the work to make sure the diff is empty.
1 parent e611daa commit 955a200

File tree

16 files changed

+431
-97
lines changed

16 files changed

+431
-97
lines changed

conf.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ contents:
549549
single: 1
550550
tags: Clients/Perl
551551
subject: Clients
552+
direct_html: true
552553
sources:
553554
-
554555
repo: elasticsearch-perl

doc_build_aliases.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ alias docbldnet='$GIT_HOME/docs/build_docs --doc $GIT_HOME/elasticsearch-net/doc
155155

156156
alias docbldphp='$GIT_HOME/docs/build_docs --doc $GIT_HOME/elasticsearch-php/docs/index.asciidoc'
157157

158-
alias docbldepl='$GIT_HOME/docs/build_docs --doc $GIT_HOME/elasticsearch/docs/perl/index.asciidoc --single'
158+
alias docbldepl='$GIT_HOME/docs/build_docs --direct_html --doc $GIT_HOME/elasticsearch/docs/perl/index.asciidoc --single'
159159

160160
alias docbldepy='$GIT_HOME/docs/build_docs --doc $GIT_HOME/elasticsearch/docs/python/index.asciidoc --single'
161161

integtest/html_diff

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ def normalize_html(html):
2626
crunched = NavigableString(re.sub(r'\s+', ' ', part))
2727
if crunched != part:
2828
part.replace_with(crunched)
29+
# Asciidoctor adds a "content" wrapper. It doesn't really change the layout
30+
# so we're ok with it.
31+
for e in soup.select('#content'):
32+
e.unwrap()
33+
# Asciidoctor adds a "ulist" class to all unordered lists which doesn't
34+
# hurt anything so we can ignore it.
35+
for e in soup.select('.itemizedlist.ulist'):
36+
e['class'].remove('ulist')
37+
# Docbook adds type="disc" to ul which is the default and isn't needed.
38+
for e in soup.select('ul'):
39+
if 'type' in e.attrs and e['type'] == 'disc':
40+
del e['type']
2941
# Format the html with indentation so we can *see* things
3042
html = soup.prettify()
3143
# docbook spits out the long-form charset and asciidoctor spits out the

integtest/spec/all_books_change_detection_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,11 @@ def chapter(index)
439439
end
440440
context 'because the book changes from docbook to direct_html' do
441441
build_one_book_out_of_one_repo_twice(
442+
before_first_build: lambda do |src, _config|
443+
book = src.book 'Test'
444+
# For now direct_html only works with single page books.
445+
book.single = true
446+
end,
442447
before_second_build: lambda do |src, _config|
443448
book = src.book 'Test'
444449
book.direct_html = true
@@ -451,6 +456,8 @@ def chapter(index)
451456
before_first_build: lambda do |src, _config|
452457
book = src.book 'Test'
453458
book.direct_html = true
459+
# For now direct_html only works with single page books.
460+
book.single = true
454461
end,
455462
before_second_build: lambda do |src, _config|
456463
book = src.book 'Test'

integtest/spec/helper/book.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ class Book
4747
# *all* branches being live.
4848
attr_accessor :live_branches
4949

50+
##
51+
# Is the book a single page book?
52+
attr_accessor :single
53+
5054
def initialize(title, prefix)
5155
@title = title
5256
@prefix = prefix
@@ -56,7 +60,7 @@ def initialize(title, prefix)
5660
@current_branch = 'master'
5761
@lang = 'en'
5862
@respect_edit_url_overrides = @suppress_migration_warnings = false
59-
@direct_html = @noindex = false
63+
@direct_html = @noindex = @single = false
6064
@live_branches = nil
6165
end
6266

integtest/spec/helper/book_conf.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def flags_conf
5454
direct_html: @direct_html ? true : nil,
5555
respect_edit_url_overrides: @respect_edit_url_overrides ? true : nil,
5656
suppress_migration_warnings: @suppress_migration_warnings ? 1 : nil,
57+
single: @single ? 1 : nil,
5758
}
5859
end
5960
end

lib/ES/Book.pm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ sub _build_book {
273273
branch => $branch,
274274
roots => $roots,
275275
relativize => 1,
276+
direct_html => $self->{direct_html},
276277
);
277278
}
278279
else {
@@ -297,6 +298,7 @@ sub _build_book {
297298
branch => $branch,
298299
roots => $roots,
299300
relativize => 1,
301+
direct_html => $self->{direct_html},
300302
);
301303
}
302304
$checkout->rmtree;

lib/ES/Util.pm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ sub build_chunked {
105105
# Turn off style options because we'll provide our own
106106
'-a' => 'stylesheet!',
107107
'-a' => 'icons!',
108+
# Turn off asciidoctor's default footer because we make our own
109+
'-a' => 'nofooter',
108110
# Pass chunking down
109111
'-a' => 'chunk_level=' . ( $chunk + 1 ),
110112
# Lock the destination file name to one we expect
@@ -264,6 +266,8 @@ sub build_single {
264266
# Turn off style options because we'll provide our own
265267
'-a' => 'stylesheet!',
266268
'-a' => 'icons!',
269+
# Turn off asciidoctor's default footer because we make our own
270+
'-a' => 'nofooter',
267271
# Add some metadata
268272
'-a' => 'dc.type=Learn/Docs/' . $section,
269273
'-a' => 'dc.subject=' . $subject,
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# frozen_string_literal: true
2+
3+
module DocbookCompat
4+
##
5+
# Methods to munge the document at the top level. All of these are a bit
6+
# scary but required at this point for docbook compatibility.
7+
module DocMunging
8+
def munge_head(doc, html)
9+
html.gsub!(%r{<title>(.+)</title>}, '<title>\1 | Elastic</title>') ||
10+
raise("Couldn't munge <title> in #{html}")
11+
munge_meta html
12+
add_dc_meta doc, html
13+
end
14+
15+
META_VIEWPORT = <<~HTML
16+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
17+
HTML
18+
def munge_meta(html)
19+
html.gsub!(
20+
%(<meta http-equiv="X-UA-Compatible" content="IE=edge">\n), ''
21+
) || raise("Couldn't remove edge compat in #{html}")
22+
html.gsub!(META_VIEWPORT, '') ||
23+
raise("Couldn't remove viewport in #{html}")
24+
html.gsub!(/<meta name="generator" content="Asciidoctor [^"]+">\n/, '') ||
25+
raise("Couldn't remove generator in #{html}")
26+
end
27+
28+
def add_dc_meta(doc, html)
29+
meta = <<~HTML.strip
30+
<meta name="DC.type" content="#{doc.attr 'dc.type'}"/>
31+
<meta name="DC.subject" content="#{doc.attr 'dc.subject'}"/>
32+
<meta name="DC.identifier" content="#{doc.attr 'dc.identifier'}"/>
33+
HTML
34+
html.gsub!('</title>', "</title>\n#{meta}") ||
35+
raise("Couldn't add dc meta to #{html}")
36+
end
37+
38+
def munge_body(doc, html)
39+
wrapped_body = <<~HTML.strip
40+
<body>
41+
<div class="#{doc.doctype}" lang="#{doc.attr 'lang', 'en'}">
42+
HTML
43+
html.gsub!(/<body[^>]+>/, wrapped_body) ||
44+
raise("Couldn't wrap body in #{html}")
45+
html.gsub!('</body>', '</div></body>') ||
46+
raise("Couldn't wrap body in #{html}")
47+
end
48+
49+
def munge_title(doc, html)
50+
header_start = <<~HTML.strip
51+
<div class="titlepage"><div><div>
52+
<h1 class="title"><a id="id-1"></a>#{doc.title}</h1>
53+
</div></div><hr></div>
54+
HTML
55+
html.gsub!(%r{<div id="header">\n<h1>.+</h1>\n</div>}, header_start) ||
56+
raise("Couldn't wrap header in #{html}")
57+
end
58+
end
59+
end
Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require 'asciidoctor/extensions'
4+
require_relative 'doc_munging'
45
require_relative '../delegating_converter'
56

67
##
@@ -15,6 +16,8 @@ def self.activate(registry)
1516
##
1617
# A Converter implementation that emulates Elastic's docbook generated html.
1718
class Converter < DelegatingConverter
19+
include DocMunging
20+
1821
def initialize(delegate)
1922
super(delegate)
2023
end
@@ -25,61 +28,69 @@ def convert_document(doc)
2528
raise("Coudn't fix html in #{html}")
2629
munge_head doc, html
2730
munge_body doc, html
28-
munge_title html
31+
munge_title doc, html
2932
html
3033
end
3134

32-
def munge_head(doc, html)
33-
html.gsub!(%r{<title>(.+)</title>}, '<title>\1 | Elastic</title>') ||
34-
raise("Couldn't munge <title> in #{html}")
35-
munge_meta html
36-
add_dc_meta doc, html
35+
SECTION_WRAPPER_CLASSES = %w[unused chapter section].freeze
36+
def convert_section(node)
37+
wrapper_class = SECTION_WRAPPER_CLASSES[node.level] || "sect#{node.level}"
38+
<<~HTML
39+
<div class="#{wrapper_class}#{node.role ? " #{node.role}" : ''}">
40+
<div class="titlepage"><div><div>
41+
<h#{node.level} class="title"><a id="#{node.id}"></a>#{node.title}#{node.attr 'edit_me_link', ''}</h#{node.level}>
42+
</div></div></div>
43+
#{node.content}
44+
</div>
45+
HTML
3746
end
3847

39-
META_VIEWPORT = <<~HTML
40-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
41-
HTML
42-
def munge_meta(html)
43-
html.gsub!(
44-
%(<meta http-equiv="X-UA-Compatible" content="IE=edge">\n), ''
45-
) || raise("Couldn't remove edge compat in #{html}")
46-
html.gsub!(META_VIEWPORT, '') ||
47-
raise("Couldn't remove viewport in #{html}")
48-
html.gsub!(/<meta name="generator" content="Asciidoctor [^"]+">\n/, '') ||
49-
raise("Couldn't remove generator in #{html}")
48+
def convert_floating_title(node)
49+
tag_name = %(h#{node.level + 1})
50+
id_attribute = node.id ? %( id="#{node.id}") : ''
51+
classes = [node.style, node.role].compact
52+
<<~HTML
53+
<#{tag_name}#{id_attribute} class="#{classes.join ' '}">#{node.title}#{node.attr 'edit_me_link', ''}</#{tag_name}>
54+
HTML
5055
end
5156

52-
def add_dc_meta(doc, html)
53-
meta = <<~HTML.strip
54-
<meta name="DC.type" content="#{doc.attr 'dc.type'}"/>
55-
<meta name="DC.subject" content="#{doc.attr 'dc.subject'}"/>
56-
<meta name="DC.identifier" content="#{doc.attr 'dc.identifier'}"/>
57+
def convert_paragraph(node)
58+
<<~HTML
59+
<p>#{node.content}</p>
5760
HTML
58-
html.gsub!('</title>', "</title>\n#{meta}") ||
59-
raise("Couldn't add dc meta to #{html}")
6061
end
6162

62-
def munge_body(doc, html)
63-
wrapped_body = <<~HTML.strip
64-
<body>
65-
<div class="#{doc.doctype}" lang="#{doc.attr 'lang', 'en'}">
66-
HTML
67-
html.gsub!(/<body[^>]+>/, wrapped_body) ||
68-
raise("Couldn't wrap body in #{html}")
69-
html.gsub!('</body>', '</div></body>') ||
70-
raise("Couldn't wrap body in #{html}")
63+
def convert_inline_anchor(node)
64+
node.attributes['role'] = 'ulink'
65+
node.attributes['window'] ||= '_top'
66+
yield
7167
end
7268

73-
def munge_title(html)
74-
html.gsub!(/<div id="header">/, '<div class="titlepage"><div><div>') ||
75-
raise("Couldn't wrap header in #{html}")
76-
ided_title = <<~HTML.strip
77-
<h1 class="title">
78-
<a id="id-1"></a>
69+
def convert_listing(node)
70+
lang = node.attr 'language'
71+
<<~HTML
72+
<div class="pre_wrapper lang-#{lang}">
73+
<pre class="programlisting prettyprint lang-#{lang}">#{node.content || ''}</pre>
74+
</div>
7975
HTML
80-
html.gsub!('<h1>', ided_title) || raise("Coudln't wrap h1 in #{html}")
81-
html.gsub!("</h1>\n</div>", "\n</h1>\n</div></div><hr></div>") ||
82-
raise("Couldn't wrap h1 in #{html}")
76+
end
77+
78+
def convert_ulist(node)
79+
node.style ||= 'itemizedlist'
80+
node.items.each { |item| item.attributes['role'] ||= 'listitem' }
81+
html = yield
82+
node.items.each do |item|
83+
next unless item.text
84+
85+
html.sub!("<p>#{item.text}</p>", item.text) ||
86+
raise("Couldn't remove <p> for #{item.text} in #{html}")
87+
end
88+
html
89+
end
90+
91+
def convert_inline_quoted(node)
92+
node.attributes['role'] ||= 'literal'
93+
yield
8394
end
8495
end
8596
end

resources/asciidoctor/lib/edit_me/extension.rb

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,24 +67,45 @@ def convert_preamble(block)
6767
end
6868

6969
def convert_section(block)
70-
yield.sub '</title>' do
71-
"#{link_for block}</title>"
70+
if block.document.basebackend? 'html'
71+
block.attributes['edit_me_link'] = link_for block
72+
yield
73+
else
74+
yield.sub '</title>' do
75+
"#{link_for block}</title>"
76+
end
7277
end
7378
end
7479

7580
def convert_floating_title(block)
76-
yield.sub '</bridgehead>' do
77-
"#{link_for block}</bridgehead>"
81+
if block.document.basebackend? 'html'
82+
block.attributes['edit_me_link'] = link_for block
83+
yield
84+
else
85+
yield.sub '</bridgehead>' do
86+
"#{link_for block}</bridgehead>"
87+
end
7888
end
7989
end
8090

8191
def link_for(block)
8292
url = edit_url block
83-
if url
84-
%(<ulink role="edit_me" url="#{url}">Edit me</ulink>)
85-
else
86-
''
87-
end
93+
return '' unless url
94+
return html_link_for url if block.document.basebackend? 'html'
95+
96+
docbook_link_for url
97+
end
98+
99+
def html_link_for(url)
100+
<<~HTML.strip
101+
<a class="edit_me" rel="nofollow" title="Edit this page on GitHub" href="#{url}">edit</a>
102+
HTML
103+
end
104+
105+
def docbook_link_for(url)
106+
<<~DOCBOOK.strip
107+
<ulink role="edit_me" url="#{url}">Edit me</ulink>
108+
DOCBOOK
88109
end
89110

90111
def edit_url(block)

resources/asciidoctor/lib/extensions.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
# for EditMe to get a nice location.
2222
document.sourcemap = true
2323
end
24+
# Adding DocbookCompat first lets it help rendering things like the
25+
# edit_me links
26+
Asciidoctor::Extensions.register DocbookCompat
2427
Asciidoctor::Extensions.register CareAdmonition
2528
Asciidoctor::Extensions.register ChangeAdmonition
2629
Asciidoctor::Extensions.register Chunker
2730
Asciidoctor::Extensions.register CopyImages
28-
Asciidoctor::Extensions.register DocbookCompat
2931
Asciidoctor::Extensions.register EditMe
3032
Asciidoctor::Extensions.register OpenInWidget
3133
Asciidoctor::Extensions.register RelativizeLink

0 commit comments

Comments
 (0)