asciidoctor-rhrev 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,8 @@
17
17
  // of their own. A marked one rolls up as a bullet into its nearest
18
18
  // enclosing section's entry instead (or the document-level -all entry, if
19
19
  // it has no enclosing section, or its enclosing section has no id).
20
+ const { liftCellTextMarks } = require('./cell-text-marks.js')
21
+
20
22
  const ROLLUP_CONTEXTS = ['paragraph', 'ulist', 'olist', 'dlist', 'admonition', 'open', 'quote', 'verse', 'sidebar']
21
23
 
22
24
  // Row-level: gets its own revision-history row when marked, requires an id.
@@ -122,6 +124,15 @@ function propagateCellContentMarks(doc, prefix) {
122
124
  const revnumber = doc.getAttribute('revnumber')
123
125
  const changeAttr = (changeBarsEnabled && revnumber) ? prefix + String(revnumber).replace(/\./g, '-') : null
124
126
 
127
+ // First the raw-text rule (cell-text-marks.js): an rhrev attribute list
128
+ // alone on the first line of any cell's content, any style, any row
129
+ // kind, lifted onto the cell and stripped from the raw text. This is the
130
+ // only path a header-row cell has, since Asciidoctor never parses its
131
+ // content as blocks. The role span it wraps a marked plain cell's text in
132
+ // is what patchConvertTableForCellTagging's HTML lift finds for such a
133
+ // cell, the same way a marked nested node's role serves an a| cell.
134
+ liftCellTextMarks(doc, prefix, { changeAttr, wrapForTag: !!changeAttr })
135
+
125
136
  doc.findBy((n) => n.getContext() === 'table').forEach((table) => {
126
137
  const rows = table.getRows()
127
138
  const cells = [].concat(rows.head.flat(), rows.foot.flat(), rows.body.flat())
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { liftCellTextMarks } = require('./cell-text-marks.js')
4
+
3
5
  /**
4
6
  * Antora pipeline extension for revision history (rhrev).
5
7
  *
@@ -337,6 +339,10 @@ function buildComponentCatalog(pageFiles, logger, quiet) {
337
339
  // own findBy. Same gotcha as antora-rhrev-asciidoc.js and the Ruby
338
340
  // gem's converter.rb. Copies the mark onto the enclosing cell (for the
339
341
  // revision-history entry below) and strips it from the nested node.
342
+ // The raw-text rule runs first (cell-text-marks.js): an rhrev attribute
343
+ // list alone on the first line of any cell's content, any style, any
344
+ // row kind, lifted onto the cell. The only path a header-row cell has.
345
+ liftCellTextMarks(doc, prefix)
340
346
  doc.findBy((n) => n.getContext() === 'table').forEach((table) => {
341
347
  const rows = table.getRows()
342
348
  const cells = [].concat(rows.head.flat(), rows.foot.flat(), rows.body.flat())
@@ -0,0 +1,84 @@
1
+ 'use strict'
2
+
3
+ // Lifts an rhrev attribute list written as the first line of a table
4
+ // cell's content onto the cell itself, then removes that line from the
5
+ // cell's raw text. Mirrors lib/asciidoctor/rhrev/cell_text_marks.rb on the
6
+ // Ruby side; see that file for the full reasoning. In short: Asciidoctor
7
+ // parses a header-row cell with no style at all (the a| prefix is consumed
8
+ // and ignored there), rows promoted into the header after parsing keep
9
+ // their style but render through the plain-text path anyway, and both
10
+ // leave the attribute list sitting in the cell's raw text. Reading that raw
11
+ // text directly covers every cell style and every row kind with one rule:
12
+ // an rhrev attribute list alone on the first line of the cell's content
13
+ // marks the cell.
14
+ //
15
+ // Reads and writes the cell's raw @text through the property Opal stores
16
+ // it under (`cell.text`), never getText(), which applies substitutions.
17
+ // Idempotent: once the line is stripped a second pass finds nothing.
18
+ //
19
+ // Parses the attribute list with Asciidoctor's own AttributeList, reached
20
+ // through the Opal runtime the caller's @asciidoctor/core already loaded
21
+ // (global.Opal), so the accepted syntax matches the Ruby side exactly. When
22
+ // no Opal runtime is present the cell is left untouched.
23
+ //
24
+ // wrapForTag: when set, and the lifted attributes include changeAttr (the
25
+ // current revision's own attribute, change bars enabled), the remaining
26
+ // cell text is wrapped in an unconstrained role span carrying the
27
+ // rhrev-changed role. Antora's HTML pipeline cannot tag a cell's own
28
+ // <td>/<th> from a treeprocessor; antora-rhrev-asciidoc.js's converter
29
+ // patch lifts a marker found inside the rendered cell onto the enclosing
30
+ // cell tag instead, and a cell that never parses its content as blocks has
31
+ // no nested node to carry that marker, so the span supplies it. Never
32
+ // applied to an AsciiDoc-style cell: its raw text is not what renders
33
+ // (the inner document is), and a head cell of that style renders the raw
34
+ // text verbatim, where the span syntax would show as literal characters.
35
+ // Never applied to a literal cell either: its content renders verbatim,
36
+ // so no inline markup survives and the span would show as characters.
37
+ // A literal cell's mark still reaches the revision history; only the
38
+ // visible tag on its own <td> is unavailable in Antora output.
39
+
40
+ const LEADING_ATTRIBUTE_LIST_RX = /^\[([^\[\]\n]+)\]\n/
41
+
42
+ function parseAttributeList (list) {
43
+ const Opal = global.Opal
44
+ const AttributeList = Opal && Opal.Asciidoctor && Opal.Asciidoctor.AttributeList
45
+ if (!AttributeList) return null
46
+ const parsed = AttributeList.$new(list).$parse()
47
+ const attrs = {}
48
+ parsed.$to_a().forEach((pair) => { attrs[String(pair[0])] = pair[1] })
49
+ return attrs
50
+ }
51
+
52
+ // Lifts a mark from one cell. Returns true when the cell changed.
53
+ function liftCellTextMark (cell, prefix, { changeAttr = null, wrapForTag = false } = {}) {
54
+ const raw = cell.text
55
+ if (typeof raw !== 'string') return false
56
+ const m = LEADING_ATTRIBUTE_LIST_RX.exec(raw)
57
+ if (!m) return false
58
+ const attrs = parseAttributeList(m[1])
59
+ if (!attrs) return false
60
+ const markKeys = Object.keys(attrs).filter((key) => key.startsWith(prefix))
61
+ if (markKeys.length === 0) return false
62
+ markKeys.forEach((key) => cell.setAttribute(key, attrs[key]))
63
+ let rest = raw.slice(m[0].length)
64
+ const style = cell.getStyle()
65
+ if (wrapForTag && changeAttr && markKeys.includes(changeAttr) && style !== 'asciidoc' && style !== 'literal') {
66
+ rest = `[.rhrev-changed]##${rest}##`
67
+ }
68
+ cell.text = rest
69
+ return true
70
+ }
71
+
72
+ // Lifts marks from every cell of every table in doc. Returns the number of
73
+ // cells changed.
74
+ function liftCellTextMarks (doc, prefix, options = {}) {
75
+ let lifted = 0
76
+ doc.findBy((n) => n.getContext() === 'table').forEach((table) => {
77
+ const rows = table.getRows()
78
+ const cells = [].concat(rows.head.flat(), rows.body.flat(), rows.foot.flat())
79
+ cells.forEach((cell) => { if (liftCellTextMark(cell, prefix, options)) lifted++ })
80
+ })
81
+ return lifted
82
+ }
83
+
84
+ module.exports = { liftCellTextMarks, liftCellTextMark, LEADING_ATTRIBUTE_LIST_RX }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asciidoctor-rhrev",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Revision history block macro extension for Asciidoctor.js and Antora (HTML5 backend)",
5
5
  "main": "antora-extension/antora-rhrev-asciidoc.js",
6
6
  "scripts": {
@@ -27,6 +27,7 @@
27
27
  "antora-extension/antora-rhrev-assembler-pagerhref.js",
28
28
  "antora-extension/antora-rhrev-html.js",
29
29
  "antora-extension/antora-rhrev-placeholder.js",
30
+ "antora-extension/cell-text-marks.js",
30
31
  "README.adoc",
31
32
  "LICENSE"
32
33
  ]