From 2946cc80314aa2b3f653c83e34ccb7aeb1db44d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= Date: Sat, 20 Jul 2024 19:03:06 +0200 Subject: [PATCH 1/3] doc: introduce a synopsis typesetting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to follow the common manpage usage, the synopsis of the commands needs to be heavily typeset. A first try was performed with using native markup, but it turned out to make the document source almost unreadable, difficult to write and prone to mistakes with unwanted Asciidoc's role attributes. In order to both simplify the writer's task and obtain a consistant typesetting in the synopsis, a custom 'synopsis' paragraph type is created and the processor for backticked text are modified. The backends of asciidoc and asciidoctor take in charge to correctly add the required typesetting. Signed-off-by: Jean-Noël Avila --- Documentation/asciidoc.conf | 20 ++++++ Documentation/asciidoctor-extensions.rb | 87 +++++++++++++++++++++++++ ci/install-dependencies.sh | 1 + t/t0450-txt-doc-vs-help.sh | 11 ++-- 4 files changed, 112 insertions(+), 7 deletions(-) diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index 60f76f43edab75..f6da6d1fbd2b20 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -28,6 +28,10 @@ ifdef::backend-docbook[] {0#} {0#{target}{0}} {0#} + +[literal-inlinemacro] +{eval:re.sub(r'(<[-a-zA-Z0-9.]+>)', r'\1', re.sub(r'([\[\s|()>]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@,\/_^\$]+\.?)+)',r'\1\2', re.sub(r'(\.\.\.?)([^\]$.])', r'\1\2', macros.passthroughs[int(attrs['passtext'][1:-1])] if attrs['passtext'][1:-1].isnumeric() else attrs['passtext'][1:-1])))} + endif::backend-docbook[] ifdef::backend-docbook[] @@ -56,4 +60,20 @@ ifdef::backend-xhtml11[] git-relative-html-prefix= [linkgit-inlinemacro] {target}{0?({0})} + +[literal-inlinemacro] +{eval:re.sub(r'(<[-a-zA-Z0-9.]+>)', r'\1', re.sub(r'([\[\s|()>]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@,\/_^\$]+\.?)+)',r'\1\2', re.sub(r'(\.\.\.?)([^\]$.])', r'\1\2', macros.passthroughs[int(attrs['passtext'][1:-1])] if attrs['passtext'][1:-1].isnumeric() else attrs['passtext'][1:-1])))} + +endif::backend-xhtml11[] + +ifdef::backend-docbook[] +ifdef::doctype-manpage[] +[paradef-default] +synopsis-style=template="verseparagraph",filter="sed 's!…\\(\\]\\|$\\)!\\0!g;s!\\([\\[ |()]\\|^\\|\\]\\|>\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.]\\+\\|…\\)!\\1\\2!g;s!<[-a-zA-Z0-9.]\\+>!\\0!g'" +endif::doctype-manpage[] +endif::backend-docbook[] + +ifdef::backend-xhtml11[] +[paradef-default] +synopsis-style=template="verseparagraph",filter="sed 's!…\\(\\]\\|$\\)!\\0!g;s!\\([\\[ |()]\\|^\\|\\]\\|>\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.]\\+\\|…\\)!\\1\\2!g;s!<[-a-zA-Z0-9.]\\+>!\\0!g'" endif::backend-xhtml11[] diff --git a/Documentation/asciidoctor-extensions.rb b/Documentation/asciidoctor-extensions.rb index d906a008039cf5..cb24480b63d2ec 100644 --- a/Documentation/asciidoctor-extensions.rb +++ b/Documentation/asciidoctor-extensions.rb @@ -1,5 +1,7 @@ require 'asciidoctor' require 'asciidoctor/extensions' +require 'asciidoctor/converter/docbook5' +require 'asciidoctor/converter/html5' module Git module Documentation @@ -39,10 +41,95 @@ def process document, output output end end + + class SynopsisBlock < Asciidoctor::Extensions::BlockProcessor + + use_dsl + named :synopsis + parse_content_as :simple + + def process parent, reader, attrs + outlines = reader.lines.map do |l| + l.gsub(/(\.\.\.?)([^\]$.])/, '`\1`\2') + .gsub(%r{([\[\] |()>]|^)([-a-zA-Z0-9:+=~@,/_^\$]+)}, '\1{empty}`\2`{empty}') + .gsub(/(<[-a-zA-Z0-9.]+>)/, '__\\1__') + .gsub(']', ']{empty}') + end + create_block parent, :verse, outlines, attrs + end + end + + class GitDBConverter < Asciidoctor::Converter::DocBook5Converter + + extend Asciidoctor::Converter::Config + register_for 'docbook5' + + def convert_inline_quoted node + if (type = node.type) == :asciimath + # NOTE fop requires jeuclid to process mathml markup + asciimath_available? ? %(#{(::AsciiMath.parse node.text).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML'}) : %() + elsif type == :latexmath + # unhandled math; pass source to alt and required mathphrase element; dblatex will process alt as LaTeX math + %() + elsif type == :monospaced + node.text.gsub(/(\.\.\.?)([^\]$.])/, '\1\2') + .gsub(%r{([\[\s|()>.]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@,/_^\$]+\.{0,2})+)}, '\1\2') + .gsub(/(<[-a-zA-Z0-9.]+>)/, '\1') + else + open, close, supports_phrase = QUOTE_TAGS[type] + text = node.text + if node.role + if supports_phrase + quoted_text = %(#{open}#{text}#{close}) + else + quoted_text = %(#{open.chop} role="#{node.role}">#{text}#{close}) + end + else + quoted_text = %(#{open}#{text}#{close}) + end + node.id ? %(#{quoted_text}) : quoted_text + end + 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 + node.text.gsub(/(\.\.\.?)([^\]$.])/, '\1\2') + .gsub(%r{([\[\s|()>.]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@,/_^\$]+\.{0,2})+)}, '\1\2') + .gsub(/(<[-a-zA-Z0-9.]+>)/, '\1') + + 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 inline_macro Git::Documentation::LinkGitProcessor, :linkgit + block Git::Documentation::SynopsisBlock postprocessor Git::Documentation::DocumentPostProcessor end diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh index 4781cd20bb0921..3e3ae39cbb1171 100755 --- a/ci/install-dependencies.sh +++ b/ci/install-dependencies.sh @@ -107,6 +107,7 @@ Documentation) test -n "$ALREADY_HAVE_ASCIIDOCTOR" || sudo gem install --version 1.5.8 asciidoctor + sudo gem install concurrent-ruby ;; esac diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh index 69917d7b8459c6..f99a69ae1b74a9 100755 --- a/t/t0450-txt-doc-vs-help.sh +++ b/t/t0450-txt-doc-vs-help.sh @@ -56,14 +56,11 @@ txt_to_synopsis () { fi && b2t="$(builtin_to_txt "$builtin")" && sed -n \ - -e '/^\[verse\]$/,/^$/ { + -E '/^\[(verse|synopsis)\]$/,/^$/ { /^$/d; - /^\[verse\]$/d; - s/_//g; - s/++//g; - s/`//g; - s/{litdd}/--/g; - s/'\''\(git[ a-z-]*\)'\''/\1/g; + /^\[(verse|synopsis)\]$/d; + s/\{litdd\}/--/g; + s/'\''(git[ a-z-]*)'\''/\1/g; p; }' \ From 06b8fff6a57642aa0f6853528c00b8c30896842d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= Date: Mon, 22 Jul 2024 20:04:22 +0200 Subject: [PATCH 2/3] doc: update the guidelines to reflect the current formatting rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-Noël Avila --- Documentation/CodingGuidelines | 58 ++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index ccaea39752c9dd..13cbcf1d7a58f4 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -820,78 +820,80 @@ Markup: __ __ - A placeholder is not enclosed in backticks, as it is not a literal. - When needed, use a distinctive identifier for placeholders, usually made of a qualification and a type: __ __ - When literal and placeholders are mixed, each markup is applied for - each sub-entity. If they are stuck, a special markup, called - unconstrained formatting is required. - Unconstrained formating for placeholders is ____ - Unconstrained formatting for literal formatting is ++like this++ - `--jobs` __ - ++--sort=++____ - ____++/.git++ - ++remote.++____++.mirror++ + Git's Asciidoc processor has been tailored to treat backticked text + as complex synopsis. When literal and placeholders are mixed, you can + use the backtick notation which will take care of correctly typesetting + the content. + `--jobs ` + `--sort=` + `/.git` + `remote..mirror` + `ssh://[@][:]/` - caveat: ++ unconstrained format is not verbatim and may expand - content. Use Asciidoc escapes inside them. +As a side effect, backquoted placeholders are correctly typeset, but +this style is not recommended. Synopsis Syntax - Syntax grammar is formatted neither as literal nor as placeholder. + The synopsis (a paragraph with [synopsis] attribute) is automatically + formatted by the toolchain and does not need typesetting. A few commented examples follow to provide reference when writing or modifying command usage strings and synopsis sections in the manual pages: Possibility of multiple occurrences is indicated by three dots: - __... + ... (One or more of .) Optional parts are enclosed in square brackets: - [__...] + [...] (Zero or more of .) - ++--exec-path++[++=++____] + An optional parameter needs to be typeset with unconstrained pairs + [] + + --exec-path[=] (Option with an optional argument. Note that the "=" is inside the brackets.) - [__...] + [...] (Zero or more of . Note that the dots are inside, not outside the brackets.) Multiple alternatives are indicated with vertical bars: - [`-q` | `--quiet`] - [`--utf8` | `--no-utf8`] + [-q | --quiet] + [--utf8 | --no-utf8] Use spacing around "|" token(s), but not immediately after opening or before closing a [] or () pair: - Do: [`-q` | `--quiet`] - Don't: [`-q`|`--quiet`] + Do: [-q | --quiet] + Don't: [-q|--quiet] Don't use spacing around "|" tokens when they're used to separate the alternate arguments of an option: - Do: ++--track++[++=++(`direct`|`inherit`)]` - Don't: ++--track++[++=++(`direct` | `inherit`)] + Do: --track[=(direct|inherit)] + Don't: --track[=(direct | inherit)] Parentheses are used for grouping: - [(__ | __)...] + [(|)...] (Any number of either or . Parens are needed to make it clear that "..." pertains to both and .) - [(`-p` __)...] + [(-p )...] (Any number of option -p, each with one argument.) - `git remote set-head` __ (`-a` | `-d` | __) + git remote set-head (-a|-d|) (One and only one of "-a", "-d" or "" _must_ (no square brackets) be provided.) And a somewhat more contrived example: - `--diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]` + --diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]] Here "=" is outside the brackets, because "--diff-filter=" is a valid usage. "*" has its own pair of brackets, because it can (optionally) be specified only when one or more of the letters is From a76998d6443a5fd3de97e296223ec845413bbb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= Date: Tue, 23 Jul 2024 22:39:53 +0200 Subject: [PATCH 3/3] doc: apply synopsis simplification on git-clone and git-init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the new synopsis formatting backend, no special asciidoc markup is needed. Signed-off-by: Jean-Noël Avila --- Documentation/git-clone.txt | 78 ++++++++++++++++++------------------- Documentation/git-init.txt | 35 +++++++---------- Documentation/urls.txt | 26 ++++++------- 3 files changed, 67 insertions(+), 72 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 8e925db7e9c662..9c13f847da3ed2 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -8,16 +8,16 @@ git-clone - Clone a repository into a new directory SYNOPSIS -------- -[verse] -`git clone` [++--template=++____] - [`-l`] [`-s`] [`--no-hardlinks`] [`-q`] [`-n`] [`--bare`] [`--mirror`] - [`-o` __] [`-b` __] [`-u` __] [`--reference` __] - [`--dissociate`] [`--separate-git-dir` __] - [`--depth` __] [`--`[`no-`]{empty}`single-branch`] [`--no-tags`] - [++--recurse-submodules++[++=++____]] [++--++[++no-++]{empty}++shallow-submodules++] - [`--`[`no-`]{empty}`remote-submodules`] [`--jobs` __] [`--sparse`] [`--`[`no-`]{empty}`reject-shallow`] - [++--filter=++____] [`--also-filter-submodules`]] [`--`] __ - [__] +[synopsis] +git clone [--template=] + [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror] + [-o ] [-b ] [-u ] [--reference ] + [--dissociate] [--separate-git-dir ] + [--depth ] [--[no-]single-branch] [--no-tags] + [--recurse-submodules[=]] [--[no-]shallow-submodules] + [--[no-]remote-submodules] [--jobs ] [--sparse] [--[no-]reject-shallow] + [--filter=] [--also-filter-submodules]] [--] + [] DESCRIPTION ----------- @@ -52,7 +52,7 @@ OPTIONS to save space when possible. + If the repository is specified as a local path (e.g., `/path/to/repo`), -this is the default, and --local is essentially a no-op. If the +this is the default, and `--local` is essentially a no-op. If the repository is specified as a URL, then this flag is ignored (and we never use the local optimizations). Specifying `--no-local` will override the default when `/path/to/repo` is given, using the regular @@ -64,8 +64,8 @@ prevent the unintentional copying of files by dereferencing the symbolic links. + *NOTE*: this operation can race with concurrent modification to the -source repository, similar to running `cp -r src dst` while modifying -`src`. +source repository, similar to running `cp -r ` while modifying +__. `--no-hardlinks`:: Force the cloning process from a repository on a local @@ -101,7 +101,7 @@ If you want to break the dependency of a repository cloned with `--shared` on its source repository, you can simply run `git repack -a` to copy all objects from the source repository into a pack in the cloned repository. -`--reference`[`-if-able`] __:: +`--reference[-if-able] `:: If the reference __ is on the local machine, automatically setup `.git/objects/info/alternates` to obtain objects from the reference __. Using @@ -142,17 +142,17 @@ objects from the source repository into a pack in the cloned repository. is specified. This flag forces progress status even if the standard error stream is not directed to a terminal. -++--server-option=++__