doc: Adds xml fixing script. (see previous and next commits)

This script is used to automatically fix issues within xml documentation
files.

The script is *for now* intended to be used ad-hoc, and the commits to
be examined.

A future discussion will define whether:

* This commit and scripts are kept.
* The script is extended for common use.

The biggest issue right now with the script is that it *could* in theory
destroy a valid space-less varlistentry.

The script could, in practical use, be changed and extended to normalize
some parts of the XML files, mainly:

* A common quoting style for attributes
* Fix-up some weird formatting automatically that xmlformat doesn't
catch

Changed files
+136 -2
doc
nixos
+5
doc/Makefile
···
find . -iname '*.xml' -type f -print0 | xargs -0 -I{} -n1 \
xmlformat --config-file "$$XMLFORMAT_CONFIG" -i {}
+
.PHONY: fix-misc-xml
+
fix-misc-xml:
+
find . -iname '*.xml' -type f \
+
-exec ../nixos/doc/varlistentry-fixer.rb {} ';'
+
.PHONY: clean
clean:
rm -f ${MD_TARGETS} .version manual-full.xml
+1 -1
doc/shell.nix
···
{ pkgs ? import ../. {} }:
(import ./default.nix).overrideAttrs (x: {
-
buildInputs = x.buildInputs ++ [ pkgs.xmloscopy ];
+
buildInputs = x.buildInputs ++ [ pkgs.xmloscopy pkgs.ruby ];
})
+5
nixos/doc/manual/Makefile
···
find . -iname '*.xml' -type f -print0 | xargs -0 -I{} -n1 \
xmlformat --config-file "../xmlformat.conf" -i {}
+
.PHONY: fix-misc-xml
+
fix-misc-xml:
+
find . -iname '*.xml' -type f \
+
-exec ../varlistentry-fixer.rb {} ';'
+
.PHONY: clean
clean:
rm -f manual-combined.xml generated
+1 -1
nixos/doc/manual/shell.nix
···
pkgs.mkShell {
name = "nixos-manual";
-
buildInputs = with pkgs; [ xmlformat jing xmloscopy ];
+
buildInputs = with pkgs; [ xmlformat jing xmloscopy ruby ];
}
+124
nixos/doc/varlistentry-fixer.rb
···
+
#!/usr/bin/env ruby
+
+
# This script is written intended as a living, evolving tooling
+
# to fix oopsies within the docbook documentation.
+
#
+
# This is *not* a formatter. It, instead, handles some known cases
+
# where something bad happened, and fixing it manually is tedious.
+
#
+
# Read the code to see the different cases it handles.
+
#
+
# ALWAYS `make format` after fixing with this!
+
# ALWAYS read the changes, this tool isn't yet proven to be always right.
+
+
require "rexml/document"
+
include REXML
+
+
if ARGV.length < 1 then
+
$stderr.puts "Needs a filename."
+
exit 1
+
end
+
+
filename = ARGV.shift
+
doc = Document.new(File.open(filename))
+
+
$touched = false
+
+
# Fixing varnames having a sibling element without spacing.
+
# This is to fix an initial `xmlformat` issue where `term`
+
# would mangle as spaces.
+
#
+
# <varlistentry>
+
# <term><varname>types.separatedString</varname><replaceable>sep</replaceable> <----
+
# </term>
+
# ...
+
#
+
# Generates: types.separatedStringsep
+
# ^^^^
+
#
+
# <varlistentry xml:id='fun-makeWrapper'>
+
# <term>
+
# <function>makeWrapper</function><replaceable>executable</replaceable><replaceable>wrapperfile</replaceable><replaceable>args</replaceable> <----
+
# </term>
+
#
+
# Generates: makeWrapperexecutablewrapperfileargs
+
# ^^^^ ^^^^ ^^ ^^
+
#
+
# <term>
+
# <option>--option</option><replaceable>name</replaceable><replaceable>value</replaceable> <-----
+
# </term>
+
#
+
# Generates: --optionnamevalue
+
# ^^ ^^
+
doc.elements.each("//varlistentry/term") do |term|
+
["varname", "function", "option", "replaceable"].each do |prev_name|
+
term.elements.each(prev_name) do |el|
+
if el.next_element and
+
el.next_element.name == "replaceable" and
+
el.next_sibling_node.class == Element
+
then
+
$touched = true
+
term.insert_after(el, Text.new(" "))
+
end
+
end
+
end
+
end
+
+
+
+
# <cmdsynopsis>
+
# <command>nixos-option</command>
+
# <arg>
+
# <option>-I</option><replaceable>path</replaceable> <------
+
# </arg>
+
#
+
# Generates: -Ipath
+
# ^^
+
doc.elements.each("//cmdsynopsis/arg") do |term|
+
["option", "replaceable"].each do |prev_name|
+
term.elements.each(prev_name) do |el|
+
if el.next_element and
+
el.next_element.name == "replaceable" and
+
el.next_sibling_node.class == Element
+
then
+
$touched = true
+
term.insert_after(el, Text.new(" "))
+
end
+
end
+
end
+
end
+
+
# <cmdsynopsis>
+
# <arg>
+
# <group choice='req'>
+
# <arg choice='plain'>
+
# <option>--profile-name</option>
+
# </arg>
+
#
+
# <arg choice='plain'>
+
# <option>-p</option>
+
# </arg>
+
# </group><replaceable>name</replaceable> <----
+
# </arg>
+
#
+
# Generates: [{--profile-name | -p }name]
+
# ^^^^
+
doc.elements.each("//cmdsynopsis/arg") do |term|
+
["group"].each do |prev_name|
+
term.elements.each(prev_name) do |el|
+
if el.next_element and
+
el.next_element.name == "replaceable" and
+
el.next_sibling_node.class == Element
+
then
+
$touched = true
+
term.insert_after(el, Text.new(" "))
+
end
+
end
+
end
+
end
+
+
+
if $touched then
+
doc.context[:attribute_quote] = :quote
+
doc.write(output: File.open(filename, "w"))
+
end