diff --git a/docs/src/Submakefile b/docs/src/Submakefile index 911289bdd03..08e1706dc8f 100644 --- a/docs/src/Submakefile +++ b/docs/src/Submakefile @@ -743,7 +743,7 @@ $(foreach L,$(LANGUAGES), \ # from docs/build/man//manN. cssrel is ../../../ for both: each output # sits at docs/build/html//man/manN/X.html (4 levels under html/). define MAN_HTML_RULE -$(DOC_OUT_HTML)/$(1)/man/%.html: $(2)/% $(DOC_SRCDIR)/docinfo.html $(DOC_SRCDIR)/docinfo-header.html +$(DOC_OUT_HTML)/$(1)/man/%.html: $(2)/% $(DOC_SRCDIR)/docinfo.html $(DOC_SRCDIR)/docinfo-header.html | manpages @$$(ECHO) Formatting $$(notdir $$<) as HTML @mkdir -p $$(dir $$@) $$(Q)if grep -q '^\.so' $$<; then \ @@ -771,12 +771,14 @@ $(DOC_OUT_HTML)/$(1)/man/%.html: $(2)/% $(DOC_SRCDIR)/docinfo.html $(DOC_SRCDIR) -r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_hal.rb \ -r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_ngc.rb \ -r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_ini.rb \ + -r $$(realpath $$(DOC_SRCDIR))/extensions/man_xref.rb \ --doctype=manpage \ --backend=html5 \ -a compat-mode \ -a mansource=LinuxCNC \ -a manmanual='LinuxCNC Documentation' \ -a "lcnc-cssrel=../../../" \ + -a "manxref-root=$$(realpath $(2))" \ -a "lcnc-lang-label=$(LANG_LABEL_$(1))" \ -a "lcnc-subpath=$$(patsubst $(DOC_OUT_HTML)/$(1)/%,%,$$@)" \ -a docinfo=shared \ @@ -1203,6 +1205,14 @@ $(DOC_OUT_ADOC)/%.html: LCNC_CSSREL=$(shell python3 -c "print('../' * '$*'.count # of the output depth. Add the +1 here, matching the $(DOC_SRCDIR) rule above. $(DOC_OUT_ADOC)/en/%.html: LCNC_CSSREL=$(shell python3 -c "print('../' * (1 + '$*'.count('/')))") +# man_xref linkbase: relative path from this html page to its language's +# man/man/ dirs. CSSREL points one level above the lang root (at the html +# root, where the css lives); the man tree sits inside the lang root, so this +# is CSSREL with one fewer "../" plus "man/". Translated stems include the +# lang dir; the English stem omits it, hence the extra step, mirroring CSSREL. +$(DOC_OUT_ADOC)/%.html: MANXREF_LINKBASE=$(shell python3 -c "print('../' * ('$*'.count('/') - 1) + 'man/')") +$(DOC_OUT_ADOC)/en/%.html: MANXREF_LINKBASE=$(shell python3 -c "print('../' * '$*'.count('/') + 'man/')") + # asciidoctor HTML rule used for every language. # $1 lang tag (en, de, ...) # $2 source root: $(DOC_SRCDIR) for English (DOC_SRCS_EN paths have no @@ -1214,13 +1224,16 @@ $(DOC_OUT_ADOC)/en/%.html: LCNC_CSSREL=$(shell python3 -c "print('../' * (1 + '$ define ASCIIDOCTOR_HTML_RULE # Order-only dep on .adoc-images-stamp so translated images are staged before # the resolver probes for them at render (it also falls back to docs/src). -$$(patsubst %.adoc,$2/%.html,$$(DOC_SRCS_$(call toUC,$1)_SMALL)): $2/%.html: $2/%.adoc $$(DOC_SRCDIR)/docinfo.html $$(DOC_SRCDIR)/docinfo-header.html | .adoc-images-stamp +$$(patsubst %.adoc,$2/%.html,$$(DOC_SRCS_$(call toUC,$1)_SMALL)): $2/%.html: $2/%.adoc $$(DOC_SRCDIR)/docinfo.html $$(DOC_SRCDIR)/docinfo-header.html | .adoc-images-stamp manpages $$(ECHO) "Building '$1' adoc to html: " $$< $$(Q)asciidoctor -r $$(realpath $$(DOC_SRCDIR))/extensions/xref_resolver.rb \ -r $$(realpath $$(DOC_SRCDIR))/extensions/image_resolver.rb \ -r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_hal.rb \ -r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_ngc.rb \ -r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_ini.rb \ + -r $$(realpath $$(DOC_SRCDIR))/extensions/man_xref.rb \ + -a "manxref-root=$$(realpath $$(if $$(filter en,$1),$$(DOC_MAN),$$(DOC_MAN)/$1))" \ + -a "manxref-linkbase=$$(MANXREF_LINKBASE)" \ -a compat-mode \ -a "doc-languages=$$(LANGUAGES)" \ -a "lcnc-lang=$1" \ diff --git a/docs/src/drivers/hostmot2.adoc b/docs/src/drivers/hostmot2.adoc index e18613f62de..454c17dbba1 100644 --- a/docs/src/drivers/hostmot2.adoc +++ b/docs/src/drivers/hostmot2.adoc @@ -521,7 +521,7 @@ Each StepGen instance has the following pins and parameters: position = counts / position_scale * 'step_type' - (u32, RW) Output format, like the step_type modparam to - the software stegen(9) component. + the software stepgen(9) component. 0 = Step/Dir, 1 = Up/Down, 2 = Quadrature. In Quadrature mode (step_type=2), the stepgen outputs one complete Gray cycle (00 \-> 01 \-> 11 \-> 10 \-> 00) for each 'step' it takes. diff --git a/docs/src/extensions/man_xref.rb b/docs/src/extensions/man_xref.rb new file mode 100644 index 00000000000..0bc2f7105f7 --- /dev/null +++ b/docs/src/extensions/man_xref.rb @@ -0,0 +1,162 @@ +# docs/src/extensions/man_xref.rb +# +# Asciidoctor treeprocessor that turns manpage cross-references in the +# conventional name(section) form (e.g. "halcmd(1)") into link: macros +# pointing at the sibling HTML page man
/.
.html. +# Running on the parsed AST (before conversion) rewrites text in AsciiDoc +# source space, so code blocks, monospace spans, passthroughs and existing +# links are excluded by node context instead of by guessing at tag +# boundaries in finished HTML. +# +# * Index-gated: a token is linked only when a page .
exists +# in the troff tree, so false positives never resolve and stay plain text +# ("feed(2)"/"arc(3)" enum values in motion(9); external open(2)/udev(8); +# typo'd or renamed API names). +# * Never links a page to itself. +# * Skips verbatim blocks; inside inline text skips monospace spans, +# pass:[] passthroughs, link:/xref:/image: macros, URLs and <>. +# * Case-insensitive match ("AXIS(1)" => axis.1); visible text kept verbatim. +# +# Used for both the HTML manpages (sibling links under man/) and the narrative +# manuals (User/HAL/Integrator), which reference man pages in the same +# name(section) form and now link across into ../man/man/. +# +# manxref-root (passed from the Submakefile) points at the troff man tree +# docs/build/man[/], whose man/ dirs enumerate every page including +# generated component pages and .so stubs. Absent => no-op, safe to always load. +# manxref-linkbase is the relative path from the page to those man/ dirs +# (default "../" for a sibling manpage; narrative pages pass their own depth). + +require 'asciidoctor' +require 'asciidoctor/extensions' + +module LinuxCNCDocs + class ManXref < Asciidoctor::Extensions::Treeprocessor + SECTIONS = %w[1 3 9].freeze + + # One name(section) token. Name starts with a letter/underscore so + # version-like "3.5(1)" never matches; the section is a single digit. + TOKEN = /\b([A-Za-z_][A-Za-z0-9_.\-]*)\((\d)\)/.freeze + + # Inline source spans that must never be rewritten. Note compat-mode + # legacy 'quotes' are emphasis (), not code, so they stay linkable. + PROTECTED_SPAN = %r{( + `[^`\n]*` + | pass:\[[^\]\n]*\] + | (?:link|xref|image):[^\s\[]*\[[^\]\n]*\] + | <<[^>\n]*>> + | https?://[^\s\[]+(?:\[[^\]\n]*\])? + )}x.freeze + + @index_cache = {} + class << self; attr_reader :index_cache; end + + # Build "name-downcased\tsection" => "man/..html" from + # the troff man tree. Filenames are the authoritative existence list. + def self.build_index(root) + key = File.expand_path(root) + cached = index_cache[key] + return cached if cached + + idx = {} + SECTIONS.each do |sec| + dir = File.join(root, "man#{sec}") + next unless File.directory?(dir) + suffix = ".#{sec}" + Dir.foreach(dir) do |fn| + next unless fn.end_with?(suffix) + name = fn[0...-suffix.length] + next if name.empty? + idx["#{name.downcase}\t#{sec}"] = "man#{sec}/#{fn}.html" + end + end + index_cache[key] = idx + end + + def process(document) + return unless document.backend.start_with?('html') + + root = document.attr('manxref-root') + return if root.nil? || root.empty? + + idx = self.class.build_index(root) + return if idx.empty? + + base = document.attr('manxref-linkbase') + base = '../' if base.nil? || base.empty? + + ctx = { + idx: idx, + base: base, + self_name: (document.attr('mantitle') || '').downcase, + self_vol: (document.attr('manvolnum') || '').to_s, + } + document.blocks.each { |blk| rewrite_block(blk, ctx) } + nil + end + + private + + # ListItem, ListTerm and Table::Cell #text getters apply inline + # substitutions; read and write the raw text to avoid double substitution. + def raw_text(node) + node.instance_variable_get(:@text) + end + + def set_raw_text(node, text) + node.instance_variable_set(:@text, text) + end + + def rewrite_block(blk, ctx) + case blk.context + when :list_item + set_raw_text(blk, rewrite_line(raw_text(blk), ctx)) if blk.text? + when :table + rewrite_table(blk, ctx) + else + blk.lines.map! { |line| rewrite_line(line, ctx) } if blk.content_model == :simple + end + return unless blk.blocks? + blk.blocks.each do |child| + if child.is_a?(Array) + # dlist entry: [terms, description] + terms, desc = child + terms.each { |t| set_raw_text(t, rewrite_line(raw_text(t), ctx)) } if terms + rewrite_block(desc, ctx) if desc + else + rewrite_block(child, ctx) + end + end + end + + def rewrite_table(tbl, ctx) + (tbl.rows.head + tbl.rows.body + tbl.rows.foot).each do |row| + row.each do |cell| + next if cell.style == :asciidoc + set_raw_text(cell, rewrite_line(raw_text(cell), ctx)) + end + end + end + + # Link tokens only in unprotected text (protected spans land on odd + # indices after the split). + def rewrite_line(line, ctx) + line.split(PROTECTED_SPAN).each_with_index.map do |part, i| + i.odd? ? part : part.gsub(TOKEN) { link_token(Regexp.last_match, ctx) } + end.join + end + + def link_token(match, ctx) + whole = match[0] + return whole if match[1].downcase == ctx[:self_name] && match[2] == ctx[:self_vol] + href = ctx[:idx]["#{match[1].downcase}\t#{match[2]}"] + # compat-mode swallows link-macro attributes; put the styling role + # on a wrapping span instead of on the anchor. + href ? %([.man-xref]#link:#{ctx[:base]}#{href}[#{whole}]#) : whole + end + end +end + +Asciidoctor::Extensions.register do + treeprocessor LinuxCNCDocs::ManXref +end diff --git a/docs/src/lcnc-overrides.css b/docs/src/lcnc-overrides.css index 4c33d707b6c..369e65d9e67 100644 --- a/docs/src/lcnc-overrides.css +++ b/docs/src/lcnc-overrides.css @@ -35,6 +35,11 @@ body:is(.article,.book,.manpage) mark { color: inherit; } +.man-xref { + font-family: monospace; + font-size: 90%; +} + /* Headings + block titles: asciidoctor default is #ba3925 (brick red). Use near-black / very dark blue. Apply to h1..h6 and to .title (image/listing/table captions) so figure captions like diff --git a/docs/src/man/man1/hal_manualtoolchange.1.adoc b/docs/src/man/man1/hal_manualtoolchange.1.adoc index bc983e32c54..9a1df091b65 100644 --- a/docs/src/man/man1/hal_manualtoolchange.1.adoc +++ b/docs/src/man/man1/hal_manualtoolchange.1.adoc @@ -89,4 +89,4 @@ net tool-changed-btn hal_manualtoolchange.change_button <= parport.0.pin-15-in == SEE ALSO -motion(1), iocontrol(1), halcmd(1). +motion(9), iocontrol(1), halcmd(1). diff --git a/docs/src/man/man3/hal_add_funct_to_thread.3.adoc b/docs/src/man/man3/hal_add_funct_to_thread.3.adoc index 5e86dde1db6..d799d5154f2 100644 --- a/docs/src/man/man3/hal_add_funct_to_thread.3.adoc +++ b/docs/src/man/man3/hal_add_funct_to_thread.3.adoc @@ -48,4 +48,4 @@ Call only from realtime init code, not from other realtime or non-realtime code. == SEE ALSO -hal_thread_new(3), hal_export_funct(3) +hal_create_thread(3), hal_export_funct(3)