Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asciidoctor: Support for "open in widget" #627

Merged
merged 14 commits into from
Mar 7, 2019
72 changes: 36 additions & 36 deletions README.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -1005,12 +1005,26 @@ footnote to a particular line of code:
=== View in Console

Code blocks can be followed by a "View in Console" link which, when clicked,
will open the code snippet in Console. The snippet can either be taken directly
from the code block (`CONSOLE`), or be a link to a custom snippet.
will open the code snippet in Console. There are two ways to do this, the
"AsciiDoc" way and the "Asciidoctor" way. The "AsciiDoc" way is preferred in
the Elaticsearch repository because it can recognize it to make tests. The
"Asciidoctor" way is preferred in other books, but only if they are built with
"Asciidoctor". Try it first and if it works then use it. Otherwise, use the
"AsciiDoc" way.

.Code block with CONSOLE link
////

The tricks that we pull to make ascidoctor support // CONSOLE are windy
and force us to add subs=+macros when we render an asciidoc snippet.
We *don't* require that for normal snippets, just those that contain
asciidoc.

////

.Code block with CONSOLE link (AsciiDoc way)
==================================
[source,asciidoc]
ifdef::asciidoctor[[source,asciidoc,subs=+macros]]
ifndef::asciidoctor[[source,asciidoc]]
--
[source,js]
----------------------------------
Expand All @@ -1026,6 +1040,24 @@ GET /_search
==================================
<1> The `// CONSOLE` line must follow immediately after the code block, before any callouts.

.Code block with CONSOLE link (Asciidoctor way)
==================================
[source,asciidoc]
--
[source,console]
----------------------------------
GET /_search
{
"query": "foo bar" \<1>
}
----------------------------------

\<1> Here's the explanation
--
==================================

Both render as:

[source,js]
----------------------------------
GET /_search
Expand All @@ -1051,38 +1083,6 @@ The local web browser can be stopped with `Ctrl-C`.

================================

==== Custom Console snippets
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've dropped the docs for this because I think, at least as we have it now, it is super confusing. It isn't compatible with Elasticsearch's tests or COPY AS cURL and I expect it is pretty surprising in general.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason these custom blocks exist is for the Definitive Guide, where we wanted to display a small amount of JSON but then click through to console to show the full example, including setup etc. Other than the Def Guide, it is not really used anywhere else

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! I tracked that down.

We've talked on and off about having some way of hiding something like these custom blocks but in a way with visual feedback for what you are getting. I'm not sure the right way to do it but I could image these making a comeback eventually.

But as I have it now I'm just dropping the docs for them because I don't want people using them without really knowing what they are doing.


Sometimes you will want to show a small amount of code in the code block, but
to provide a full recreation in the Console snippet. In this case, you need to:

* Save the snippet file in the `./snippets/` directory in the root docs directory.
* Under the code block, specify the name of the snippet file with
+
// CONSOLE: path/to/snippet.json

For instance, to add a custom snippet to the file `./one/two/three.asciidoc`, save the snippet
to `./snippets/one/two/three/example_1.json`, then add the `CONSOLE` link below the code block:

.Code block with custom CONSOLE link
==================================
[source,asciidoc]
--
[source,js]
----------------------------------
GET /_search
{
"query": "foo bar" \<1>
}
----------------------------------
// CONSOLE:one/two/three/example_1.json <1>

\<1> Here's the explanation
--
<1> The path should not contain the initial `snippets` directory
==================================


[[admon-blocks]]
=== Admonition blocks

Expand Down
8 changes: 5 additions & 3 deletions integtest/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ readme_expected_files: /tmp/readme_asciidoc
%_same_files: /tmp/%_asciidoc /tmp/%_asciidoctor
diff \
<(cd /tmp/$*_asciidoc && find * -type f | sort \
| grep -v snippets/blocks \
| grep -v snippets \
) \
<(cd /tmp/$*_asciidoctor && find * -type f | sort)
<(cd /tmp/$*_asciidoctor && find * -type f | sort \
| grep -v snippets \
)
# The grep -v below are for known issues with asciidoctor
for file in $$(cd /tmp/$*_asciidoc && find * -type f -name '*.html' \
| grep -v 'blocks\|changes\|experimental'); do \
| grep -v 'changes\|experimental'); do \
./html_diff /tmp/$*_asciidoc/$$file /tmp/$*_asciidoctor/$$file; \
done

Expand Down
4 changes: 4 additions & 0 deletions integtest/html_diff
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ def normalize_html(html):
# Remove the zero width space that asciidoctor adds after each horizontal
# ellipsis. They don't hurt anything but asciidoc doesn't make them
html = html.replace('\u2026\u200b', '\u2026')
# We intentionally changed lang-js to lang-console because in Asciidoctor
# because that is more accurate
html = html.replace('"programlisting prettyprint lang-js"',
'"programlisting prettyprint lang-console"')
# Temporary workaround for known issues
html = html.replace('class="edit_me" href="/./', 'class="edit_me" href="')
html = re.sub(
Expand Down
3 changes: 2 additions & 1 deletion lib/ES/Template.pm
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ sub apply {
my $self = shift;
my $dir = shift;
my $lang = shift || die "No lang specified";
my $asciidoctor = shift;

my $map = $self->_map;

Expand All @@ -61,7 +62,7 @@ sub apply {
$contents =~ s/\s*$/\n/;

# Extract AUTOSENSE snippets
$contents = $self->_autosense_snippets( $file, $contents );
$contents = $self->_autosense_snippets( $file, $contents ) unless $asciidoctor;

# Fill in template
my @parts = @{ $self->_parts };
Expand Down
8 changes: 4 additions & 4 deletions lib/ES/Util.pm
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ sub build_chunked {
my ($chunk_dir) = grep { -d and /\.chunked$/ } $dest->children
or die "Couldn't find chunk dir in <$dest>";

finish_build( $index->parent, $chunk_dir, $lang );
finish_build( $index->parent, $chunk_dir, $lang, $asciidoctor );
extract_toc_from_index($chunk_dir);
for ( $chunk_dir->children ) {
run( 'mv', $_, $dest );
Expand Down Expand Up @@ -285,7 +285,7 @@ sub build_single {
or die "Couldn't rename <$src> to <index.html>: $!";
}

finish_build( $index->parent, $dest, $lang );
finish_build( $index->parent, $dest, $lang, $asciidoctor );
}

#===================================
Expand Down Expand Up @@ -367,10 +367,10 @@ sub build_pdf {
#===================================
sub finish_build {
#===================================
my ( $source, $dest, $lang ) = @_;
my ( $source, $dest, $lang, $asciidoctor ) = @_;

# Apply template to HTML files
$Opts->{template}->apply( $dest, $lang );
$Opts->{template}->apply( $dest, $lang, $asciidoctor );

my $snippets_dest = $dest->subdir('snippets');
my $snippets_src;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,24 @@
# Because Asciidoc permits these mismatches but asciidoctor does not. We'll
# emit a warning because, permitted or not, they are bad style.
#
# With the help of ElasticCompatTreeProcessor turns
# [source,js]
# ----
# foo
# ----
# // CONSOLE
#
# Into
# [source,console]
# ----
# foo
# ----
# Because Elastic has thousands of these constructs but Asciidoctor feels
# strongly that comments should not convey meaning. This is a totally
# reasonable stance and we should migrate away from these comments in new
# docs when it is possible. But for now we have to support the comments as
# well.
#
class ElasticCompatPreprocessor < Asciidoctor::Extensions::Preprocessor
include Asciidoctor::Logging

Expand Down Expand Up @@ -140,13 +158,21 @@ def reader.process_line(line)
@code_block_start = line
end
end

supported = 'added|coming|deprecated'
# First convert the "block" version of these macros. We convert them
# to block macros because they are at the start of the line....
line&.gsub!(/^(#{supported})\[([^\]]*)\]/, '\1::[\2]')
# Then convert the "inline" version of these macros. We convert them
# to inline macros because they are *not* at the start of the line....
line&.gsub!(/(#{supported})\[([^\]]*)\]/, '\1:[\2]')
# Transform Elastic's traditional comment based marking for
# AUTOSENSE/KIBANA/CONSOLE snippets into a marker that we can pick
# up during tree processing to turn the snippet into a marked up
# CONSOLE snippet. Asciidoctor really doesn't recommend this sort of
# thing but we have thousands of them and it'll take us some time to
# stop doing it.
line&.gsub!(%r{//\s*(?:AUTOSENSE|KIBANA|CONSOLE|SENSE:[^\n<]+)}, 'pass:[\0]')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider making the regexp a constant so that it's not instantiated each time this code is called. It also has the benefit of following this file's convention of making regexp's constants. See these lines

end
end
reader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,62 @@
# <1> The count of categories that were matched
# <2> The categories retrieved
#
# Turns
# [source,js]
# --------------------------------------------------
# GET / <1>
# --------------------------------------------------
# pass:[// CONSOLE]
# <1> The count of categories that were matched
# <2> The categories retrieved
#
# Into
# [source,console]
# --------------------------------------------------
# GET / <1>
# --------------------------------------------------
# <1> The count of categories that were matched
# <2> The categories retrieved
#
class ElasticCompatTreeProcessor < TreeProcessorScaffold
include Asciidoctor::Logging

def process_block(block)
if block.context == :listing && block.style == "source" &&
block.subs.include?(:specialcharacters) == false
# callouts have to come *after* special characters
had_callouts = block.subs.delete(:callouts)
block.subs << :specialcharacters
block.subs << :callouts if had_callouts
end
return unless block.context == :listing && block.style == 'source'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a minor Ruby style thing, but I would typically only use return if there were a specific value I was returning when a condition was met. For example, here
In this case, I would write the code like this:

if block.context == :listing && block.style == 'source'
  process_subs block
  process_lang_override block
end


process_subs block
process_lang_override block
end

def process_subs(block)
return if block.subs.include? :specialcharacters
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here. I would write this like:

if !block.subs.include? :specialcharacters
  # callouts have to come *after* special characters
  had_callouts = block.subs.delete(:callouts)
  block.subs << :specialcharacters
  block.subs << :callouts if had_callouts
end


# callouts have to come *after* special characters
had_callouts = block.subs.delete(:callouts)
block.subs << :specialcharacters
block.subs << :callouts if had_callouts
end

LANG_MAPPING = {
'AUTOSENSE' => 'sense',
'CONSOLE' => 'console',
'KIBANA' => 'kibana',
'SENSE' => 'sense',
}.freeze

def process_lang_override(block)
next_block = block.next_adjacent_block
return unless next_block && next_block.context == :paragraph
return unless next_block.source =~ %r{pass:\[//\s*([^:\]]+)(?::\s*([^\]]+))?\]}

lang = LANG_MAPPING[$1]
snippet = $2
return unless lang # Not a language we handle

block.set_attr 'language', lang
block.set_attr 'snippet', snippet

block.parent.blocks.delete next_block
block.parent.reindex_sections
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be more readable if all the logic was in one place. The same things I said above about return hold true here. I would write this code like:

def process_lang_override(block)
  next_block = block.next_adjacent_block
  unless next_block && next_block.context == :paragraph && 
                next_block.source =~ %r{pass:\[//\s*([^:\]]+)(?::\s*([^\]]+))?\]} &&
                lang = LANG_MAPPING[$1]

    snippet = $2
    block.set_attr 'language', lang
    block.set_attr 'snippet', snippet

    block.parent.blocks.delete next_block
    block.parent.reindex_sections
  end
end

end
end
2 changes: 2 additions & 0 deletions resources/asciidoctor/lib/extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require_relative 'elastic_compat_tree_processor/extension'
require_relative 'elastic_compat_preprocessor/extension'
require_relative 'elastic_include_tagged/extension'
require_relative 'open_in_widget/extension'

Asciidoctor::Extensions.register ChangeAdmonition
Asciidoctor::Extensions.register do
Expand All @@ -18,5 +19,6 @@
treeprocessor CopyImages::CopyImages
treeprocessor EditMe
treeprocessor ElasticCompatTreeProcessor
treeprocessor OpenInWidget
include_processor ElasticIncludeTagged
end
Loading