From d9070e669020ab7fd0c8dc4c24cc42a33545e135 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>
Date: Sat, 30 Nov 2024 17:03:22 +0100
Subject: [PATCH 1/2] stylesheet: remove background and border from code spans
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The new style makes the code spans lighter and more integrated into
the text. The new style also makes the code spans more readable and
less intrusive.

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 assets/sass/application.scss | 2 +-
 assets/sass/typography.scss  | 3 ---
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/assets/sass/application.scss b/assets/sass/application.scss
index 7e352c3f30..638e47bde2 100644
--- a/assets/sass/application.scss
+++ b/assets/sass/application.scss
@@ -29,7 +29,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/typography.scss b/assets/sass/typography.scss
index 5e8a840906..bd598b99e8 100644
--- a/assets/sass/typography.scss
+++ b/assets/sass/typography.scss
@@ -251,7 +251,6 @@ blockquote {
 }
 
 code {
-  @include border-radius(3px);
   display: block;
   padding: 10px 15px 13px;
   margin-bottom: 1em;
@@ -260,8 +259,6 @@ code {
   line-height: $fixed-width-line-height;
   font-variant-ligatures: none;
   color: $orange;
-  background-color: #fff;
-  border: solid 1px #efeee6;
 }
 
 // Quotes

From 87aab82c6e263f15631de2423db0ad6a55783338 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>
Date: Sun, 17 Nov 2024 22:24:18 +0100
Subject: [PATCH 2/2] manpages: prepare for new manpage format
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This commit adds a upcoming manpage format to the AsciiDoc
backend. The new format changes are:

 * The synopsis is now a section with a dedicated style. This
 "synopsis" style allows to automatically format the keywords as
 monospaced and <placeholders> as italic.
 * the backticks are now used to format synopsis-like syntax in inline
 elements.

The parsing of synopsis is done with a new AsciiDoc extension that
makes use of the PEG parser parslet.

All the asciidoc manpages sources are processed with this
extension. It may upset the formatting for older manpages, making it
not consistent across a page, but this will be a mild side effect, as
this was not really consistent before.

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 Gemfile                          |   1 +
 script/asciidoctor-extensions.rb | 129 +++++++++++++++++++++++++++++++
 script/update-docs.rb            |   1 +
 3 files changed, 131 insertions(+)
 create mode 100644 script/asciidoctor-extensions.rb

diff --git a/Gemfile b/Gemfile
index 3ce8333751..2e9ac7e322 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,3 +7,4 @@ gem "rss"
 gem "asciidoctor", "~> 2.0.0"
 gem "nokogiri"
 gem "diffy"
+gem "parslet"
diff --git a/script/asciidoctor-extensions.rb b/script/asciidoctor-extensions.rb
new file mode 100644
index 0000000000..54285f6a75
--- /dev/null
+++ b/script/asciidoctor-extensions.rb
@@ -0,0 +1,129 @@
+require 'asciidoctor'
+require 'asciidoctor/extensions'
+require 'asciidoctor/converter/html5'
+require 'parslet'
+
+module Git
+  module Documentation
+    class AdocSynopsisQuote < Parslet::Parser
+      # parse a string like "git add -p [--root=<path>]" as series of tokens keywords, grammar signs and placeholders
+      # where placeholders are UTF-8 words separated by '-', enclosed in '<' and '>'
+      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(:grammar) |
+          ignore.as(:ignore) | keyword.as(:keyword)
+      end
+      rule(:tokens) { token.repeat(1) }
+      root(:tokens)
+    end
+
+    class EscapedSynopsisQuote < Parslet::Parser
+      rule(:space)      { match('[\s\t\n ]').repeat(1) }
+      rule(:space?)     { space.maybe }
+      rule(:keyword) { match('[-a-zA-Z0-9:+=~@,\./_\^\$\'"\*%!{}#]').repeat(1) }
+      rule(:placeholder) { str('&lt;') >> match('[[:word:]]|-').repeat(1) >> str('&gt;') }
+      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(:grammar) |
+          ignore.as(:ignore) | keyword.as(:keyword)
+      end
+      rule(:tokens) { token.repeat(1) }
+      root(:tokens)
+    end
+
+    class SynopsisQuoteToAdoc < Parslet::Transform
+      rule(grammar: simple(:grammar)) { grammar.to_s }
+      rule(keyword: simple(:keyword)) { "{empty}`#{keyword}`{empty}" }
+      rule(placeholder: simple(:placeholder)) { "__#{placeholder}__" }
+      rule(ignore: simple(:ignore)) { '' }
+    end
+
+    class SynopsisQuoteToHtml5 < Parslet::Transform
+      rule(grammar: simple(:grammar)) { grammar.to_s }
+      rule(keyword: simple(:keyword)) { "<code>#{keyword}</code>" }
+      rule(placeholder: simple(:placeholder)) { "<em>#{placeholder}</em>" }
+      rule(ignore: simple(:ignore)) { '' }
+    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
+          SynopsisConverter.new.convert(
+            EscapedSynopsisQuote.new,
+            SynopsisQuoteToHtml5.new,
+            node.text,
+            node.document.logger
+          )
+        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
+              %(<span id="#{node.id}"#{class_attr}>#{open}#{node.text}#{close}</span>)
+            end
+          elsif node.role
+            if tag
+              %(#{open.chop} class="#{node.role}">#{node.text}#{close})
+            else
+              %(<span class="#{node.role}">#{open}#{node.text}#{close}</span>)
+            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 07c168fd1a..28223891ce 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"