diff --git a/Gemfile b/Gemfile index 980ffe5c19..bfcb1bca7a 100644 --- a/Gemfile +++ b/Gemfile @@ -8,5 +8,5 @@ gem "rss" gem "asciidoctor", "~> 2.0.0" gem "nokogiri" gem "diffy" - gem "base64", "~> 0.2.0" +gem "parslet" diff --git a/Gemfile.lock b/Gemfile.lock index 5a050cc9e1..e7a7fba913 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,6 +37,7 @@ GEM octokit (9.2.0) faraday (>= 1, < 3) sawyer (~> 0.9) + parslet (2.0.0) public_suffix (6.0.1) racc (1.8.1) rexml (3.4.1) @@ -64,6 +65,7 @@ DEPENDENCIES faraday-retry nokogiri octokit + parslet rss CHECKSUMS @@ -86,6 +88,7 @@ CHECKSUMS nokogiri (1.18.8-x86_64-linux-gnu) sha256=4a747875db873d18a2985ee2c320a6070c4a414ad629da625fbc58d1a20e5ecc nokogiri (1.18.8-x86_64-linux-musl) sha256=ddd735fba49475a395b9ea793bb6474e3a3125b89960339604d08a5397de1165 octokit (9.2.0) sha256=4fa47ff35ce654127edf2c836ab9269bcc8829f5542dc1e86871f697ce7f4316 + parslet (2.0.0) sha256=d45130695d39b43d7e6a91f4d2ec66b388a8d822bae38de9b4de9a5fbde1f606 public_suffix (6.0.1) sha256=61d44e1cab5cbbbe5b31068481cf16976dd0dc1b6b07bd95617ef8c5e3e00c6f racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f rexml (3.4.1) sha256=c74527a9a0a04b4ec31dbe0dc4ed6004b960af943d8db42e539edde3a871abca diff --git a/assets/sass/application.scss b/assets/sass/application.scss index e3c8833f09..176ac7f90d 100644 --- a/assets/sass/application.scss +++ b/assets/sass/application.scss @@ -32,7 +32,7 @@ $baseurl: "{{ .Site.BaseURL }}{{ if (and (ne .Site.BaseURL "/") (ne .Site.BaseUR code { display: inline; - padding: 0 5px; + padding: 0 0; } pre { diff --git a/assets/sass/man-pages.scss b/assets/sass/man-pages.scss index b3999b92a8..01b7edb836 100644 --- a/assets/sass/man-pages.scss +++ b/assets/sass/man-pages.scss @@ -51,7 +51,6 @@ word-wrap: break-word; /* Internet Explorer 5.5+ */ white-space: pre; white-space: pre-wrap; /* css-3 */ - background-color: #e8e7dd; em { font-weight: bold; @@ -59,7 +58,6 @@ } code { - background-color: #e8e7dd; margin-bottom: 0; border: none; padding-left: 0; diff --git a/assets/sass/typography.scss b/assets/sass/typography.scss index f9f7634276..74674aa594 100644 --- a/assets/sass/typography.scss +++ b/assets/sass/typography.scss @@ -260,8 +260,13 @@ code { line-height: $fixed-width-line-height; font-variant-ligatures: none; color: var(--orange); - background-color: var(--main-bg); - border: solid 1px var(--pre-border); +} + +p { + .synopsis { + @include border-radius(3px); + border: solid 1px var(--pre-border); + } } // Quotes diff --git a/script/asciidoctor-extensions.rb b/script/asciidoctor-extensions.rb new file mode 100644 index 0000000000..f68893f0dd --- /dev/null +++ b/script/asciidoctor-extensions.rb @@ -0,0 +1,124 @@ +require 'asciidoctor' +require 'asciidoctor/extensions' +require 'asciidoctor/converter/html5' +require 'parslet' +# for parslet, see https://kschiess.github.io/parslet/parser.html + +module Git + module Documentation + class AdocSynopsisQuote < Parslet::Parser + # parse a string like "git add -p [--root=]" as series of + # tokens keywords, grammar signs and placeholders where + # placeholders are UTF-8 words separated by '-', enclosed in '<' + # and '>'. The >> indicates a "simple sequence", for example + # str('...') >> match('\]|$').present? means "first match three + # periods, then ensure that they are either followed by a + # closing bracket or they are at the end. + rule(:space) { match('[\s\t\n ]').repeat(1) } + rule(:space?) { space.maybe } + rule(:keyword) { match('[-a-zA-Z0-9:+=~@,\./_\^\$\'"\*%!{}#]').repeat(1) } + rule(:placeholder) { str('<') >> match('[[:word:]]|-').repeat(1) >> str('>') } + rule(:opt_or_alt) { match('[\[\] |()]') >> space? } + rule(:ellipsis) { str('...') >> match('\]|$').present? } + rule(:grammar) { opt_or_alt | ellipsis } + rule(:ignore) { match('[\'`]') } + + rule(:token) do + grammar.as(:grammar) | placeholder.as(:placeholder) | space.as(:space) | + ignore.as(:ignore) | keyword.as(:keyword) + end + rule(:tokens) { token.repeat(1) } + root(:tokens) + end + + class EscapedSynopsisQuote < AdocSynopsisQuote + rule(:placeholder) { str('<') >> match('[[:word:]]|-').repeat(1) >> str('>') } + end + + class SynopsisQuoteBase < Parslet::Transform + rule(grammar: simple(:grammar)) { grammar.to_s } + rule(space: simple(:space)) { space.to_s } + rule(ignore: simple(:ignore)) { '' } + end + + class SynopsisQuoteToAdoc < SynopsisQuoteBase + rule(keyword: simple(:keyword)) { "{empty}`#{keyword}`{empty}" } + rule(placeholder: simple(:placeholder)) { "__#{placeholder}__" } + end + + class SynopsisQuoteToHtml5 < SynopsisQuoteBase + rule(keyword: simple(:keyword)) { "#{keyword}" } + rule(placeholder: simple(:placeholder)) { "#{placeholder}" } + end + + class SynopsisConverter + def convert(parslet_parser, parslet_transform, reader, logger = nil) + reader.lines.map do |l| + parslet_transform.apply(parslet_parser.parse(l)).join + end.join("\n") + rescue Parslet::ParseFailed + logger.info "synopsis parsing failed for '#{reader.lines.join(' ')}'" + reader.lines.map do |l| + parslet_transform.apply(placeholder: l) + end.join("\n") + end + end + + class SynopsisBlock < Asciidoctor::Extensions::BlockProcessor + use_dsl + named :synopsis + parse_content_as :simple + + def process(parent, reader, attrs) + outlines = SynopsisConverter.new.convert( + AdocSynopsisQuote.new, + SynopsisQuoteToAdoc.new, + reader, + parent.document.logger + ) + create_block parent, :verse, outlines, attrs + end + end + + # register a html5 converter that takes in charge + # to convert monospaced text into Git style synopsis + class GitHTMLConverter < Asciidoctor::Converter::Html5Converter + extend Asciidoctor::Converter::Config + register_for 'html5' + + def convert_inline_quoted(node) + if node.type == :monospaced + t = SynopsisConverter.new.convert( + EscapedSynopsisQuote.new, + SynopsisQuoteToHtml5.new, + node.text, + node.document.logger + ) + "#{t}" + else + open, close, tag = QUOTE_TAGS[node.type] + if node.id + class_attr = node.role ? %( class="#{node.role}") : '' + if tag + %(#{open.chop} id="#{node.id}"#{class_attr}>#{node.text}#{close}) + else + %(#{open}#{node.text}#{close}) + end + elsif node.role + if tag + %(#{open.chop} class="#{node.role}">#{node.text}#{close}) + else + %(#{open}#{node.text}#{close}) + end + else + %(#{open}#{node.text}#{close}) + end + end + end + end + end +end + +Asciidoctor::Extensions.register do + block Git::Documentation::SynopsisBlock +end diff --git a/script/update-docs.rb b/script/update-docs.rb index 1c5c17b424..37ca95f2ae 100644 --- a/script/update-docs.rb +++ b/script/update-docs.rb @@ -9,6 +9,7 @@ require 'yaml' require 'diffy' require_relative "version" +require_relative 'asciidoctor-extensions' SITE_ROOT = File.join(File.expand_path(File.dirname(__FILE__)), '../') DOCS_INDEX_FILE = "#{SITE_ROOT}external/docs/content/docs/_index.html"