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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +10 -0
- data/lib/asciidoctor/rhrev/cell_text_marks.rb +73 -0
- data/lib/asciidoctor/rhrev/change_bars.rb +26 -0
- data/lib/asciidoctor/rhrev/converter.rb +34 -5
- data/lib/asciidoctor/rhrev/rhrev_html.rb +9 -2
- data/lib/asciidoctor/rhrev.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f150c21ea6f2c3fe871bc18d4e91cae846279104ddc72d72e9b7f06e4a009a20
|
|
4
|
+
data.tar.gz: 5f1ef686a5575db2d83e818577310e9f81060beb891ec800abcad29f19327c2b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f6de41ae3d96fdf56c8eeda288f1bc2650d365019e641ccc5259755107e8f81af2be29715b6ef4a0c9f42e8eb22d53f7db73bfe38e2d447b19e7d1382f3e8047
|
|
7
|
+
data.tar.gz: 68251c6a35b808bd9e138cc6cfd3996969d052c20fac88e3bb8b17f5efd8b9a388a243a4c9a50471ba1e54fd95e126ca0acab584abd4c627c9a0067c84bf9d89
|
data/CHANGELOG.adoc
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
= Changelog
|
|
2
2
|
|
|
3
|
+
== 1.8.0 (2026-09-08)
|
|
4
|
+
|
|
5
|
+
* Table-cell marks no longer require the AsciiDoc-style cell prefix `a|`: an rhrev attribute list alone on the first line of any cell's content marks the cell, for every style prefix and none, and for cells in a header row, which no earlier release could mark at all. `Asciidoctor::Rhrev::CellTextMarks` reads the attribute list off the cell's raw text, sets the attributes on the cell, and strips the line, before the existing `a|` propagation runs; the PDF prescan, the HTML treeprocessor, and both Antora extension files use it. In Antora output the lift wraps a marked plain or header cell's text in an `rhrev-changed` role span so the existing table post-processor tags the cell's own `<td>`/`<th>`; a literal cell renders verbatim and stays untagged there, its mark still rolls up. Foot rows now join every table-cell walk. New fixture `tests/features/test-change-bars-table-header-cells.adoc` and regression files `tests/regression/table-header-cell-marks.rb`, `html-table-header-cell-marks.rb`, and `js-table-header-cell-marks.js`
|
|
6
|
+
* Multi-row table headers: rows promoted into the header with the `hrows` tree processor carry their marks the same way. asciidoctor-pdf drops header-cell rowspan (asciidoctor/asciidoctor-pdf#2631); the `multirow_table_header.rb` converter in baiyibai-antora-collector-assembler-pdf-themes restores it together with the `hrows` promotion
|
|
7
|
+
|
|
8
|
+
== 1.7.1 (2026-09-02)
|
|
9
|
+
|
|
10
|
+
* Fix two revision-history entries sharing the same start page sorting in the wrong relative order whenever one of them has body content that spans a page break (PDF backend). `Converter#convert_section` correctly reads a section's own starting page via `pdf-page-start`, set by asciidoctor-pdf's own `convert_section` right before the heading itself is inked, but paired it with a `:dest][:y]` value read from `cursor` after `result = super` returns, once the section's whole body (nested content, a large table, anything that can span several physical pages of its own) had already rendered. For a page-spanning section that cursor belongs to whatever page the body finished on, not the section's own start page `pdf-page-start` names, so the two stopped describing the same physical location at all; two entries sharing that start page are ordered by this `y` value alone (`renderer.rb`'s own `entries.sort_by`), and a wrong one can reverse their true top-to-bottom order. Reported directly, with real captured values from a real document, confirming exactly this: a page-spanning section's own `y` secretly belonging to the next physical page, sorting it ahead of another entry that genuinely was higher on their shared page. `ChangeBars#record_section_heading_position`, called unconditionally, not gated on change bars being enabled at all, from `ink_general_heading`/`ink_chapter_title`/`ink_part_title`, the same moment upstream itself captures `pdf-page-start`, right before the heading is actually inked. `Converter#convert_section` reads it back instead of a live post-super cursor, so `page_num` and `y` always describe the section's own start consistently. New regression file, `tests/regression/section-dest-y-page-mismatch.rb`, forces the page-spanning body with an explicit `<<<` page break rather than tuning paragraph-repeat counts against exact font metrics, deterministic regardless of theme or font; confirmed to fail (reversed order) without the fix and pass with it
|
|
11
|
+
* Note: gem-only; the Antora extension (HTML output, no PDF page/cursor concept) is unaffected, npm still bumps concurrently per the standing 1.5.1 policy
|
|
12
|
+
|
|
3
13
|
== 1.7.0 (2026-09-02)
|
|
4
14
|
|
|
5
15
|
* Replace 1.6.0's per-item list change bars with a different design, after real user feedback that the syntax it needed was too heavy: marking one item required a `+` continuation, an attribute line, and a two-line empty open block, four lines to mark a single bullet, far more than every other rhrev-markable construct's own single attribute line. 1.6.0's mechanism (`Converter#propagate_list_item_content_marks`, the attached-continuation-block workaround, `list_item` added to the roll-up contexts) is fully removed, not just superseded
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Asciidoctor
|
|
4
|
+
module Rhrev
|
|
5
|
+
# Lifts an rhrev attribute list written as the first line of a table
|
|
6
|
+
# cell's content onto the cell itself, then removes that line from the
|
|
7
|
+
# cell's raw text, so the mark reaches the cell for every cell style,
|
|
8
|
+
# including cells in a header row.
|
|
9
|
+
#
|
|
10
|
+
# Asciidoctor parses a header-row cell without any style at all:
|
|
11
|
+
# Table::Cell#initialize forces the style to nil whenever the table's
|
|
12
|
+
# header option is in effect, so the a| prefix is consumed and ignored
|
|
13
|
+
# there, and the cell's content is never parsed as blocks. Rows promoted
|
|
14
|
+
# into the header after parsing (the hrows tree processor from
|
|
15
|
+
# asciidoctor-extensions-lab) keep their parsed style, but asciidoctor-pdf
|
|
16
|
+
# and Asciidoctor's own HTML5 converter render every head cell through
|
|
17
|
+
# the plain-text path, which returns an AsciiDoc-style cell's raw source,
|
|
18
|
+
# attribute line included. Both cases leave the attribute list in the
|
|
19
|
+
# cell's raw text. Reading that raw text directly, before any converter
|
|
20
|
+
# touches it, covers every style, every row kind, and every promotion
|
|
21
|
+
# mechanism with one rule: an rhrev attribute list alone on the first
|
|
22
|
+
# line of the cell's content marks the cell.
|
|
23
|
+
#
|
|
24
|
+
# Reads and writes only the cell's raw @text. Cell#text applies
|
|
25
|
+
# substitutions, and the PDF converter's prescan runs with attribute
|
|
26
|
+
# substitution deliberately suppressed because counter attributes
|
|
27
|
+
# increment on every evaluation; table_contains_pagerhref? already takes
|
|
28
|
+
# the same care. Idempotent: once the line is stripped a second pass
|
|
29
|
+
# finds nothing to lift, which matters for asciidoctor-pdf's scratch
|
|
30
|
+
# passes and rhrev's own deferred pagerhref re-render, both of which
|
|
31
|
+
# reuse the same node objects.
|
|
32
|
+
#
|
|
33
|
+
# For an AsciiDoc-style cell the inner document was already parsed from
|
|
34
|
+
# the same raw text, with the attribute list attached to its first nested
|
|
35
|
+
# block; propagate_cell_content_marks still moves that mark onto the cell
|
|
36
|
+
# and strips it from the nested block, so the two mechanisms agree on the
|
|
37
|
+
# cell's attributes and the strip here only affects the raw text a head
|
|
38
|
+
# cell renders from.
|
|
39
|
+
#
|
|
40
|
+
# An attribute list without any rhrev-prefixed key is left untouched: it
|
|
41
|
+
# belongs to the writer's own content.
|
|
42
|
+
module CellTextMarks
|
|
43
|
+
LEADING_ATTRIBUTE_LIST_RX = /\A\[([^\[\]\n]+)\]\n/
|
|
44
|
+
|
|
45
|
+
module_function
|
|
46
|
+
|
|
47
|
+
# Lifts marks from every cell of every table in doc. Returns the number
|
|
48
|
+
# of cells changed.
|
|
49
|
+
def lift doc, prefix
|
|
50
|
+
lifted = 0
|
|
51
|
+
doc.find_by(context: :table).each do |table|
|
|
52
|
+
rows = table.rows
|
|
53
|
+
(rows.head + rows.body + rows.foot).flatten.each do |cell|
|
|
54
|
+
lifted += 1 if lift_cell cell, prefix
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
lifted
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Lifts a mark from one cell. Returns true when the cell changed.
|
|
61
|
+
def lift_cell cell, prefix
|
|
62
|
+
raw = cell.instance_variable_get :@text
|
|
63
|
+
return false unless raw.is_a?(::String) && (m = LEADING_ATTRIBUTE_LIST_RX.match raw)
|
|
64
|
+
attrs = (::Asciidoctor::AttributeList.new m[1]).parse
|
|
65
|
+
marks = attrs.select { |key, _| key.to_s.start_with? prefix }
|
|
66
|
+
return false if marks.empty?
|
|
67
|
+
marks.each { |key, value| cell.set_attr key.to_s, value }
|
|
68
|
+
cell.instance_variable_set :@text, (raw.slice m[0].length, raw.length)
|
|
69
|
+
true
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -62,6 +62,29 @@ module Asciidoctor
|
|
|
62
62
|
@change_bar_starts&.delete node
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
# A section's own cursor position at heading-ink time, before its
|
|
66
|
+
# body (potentially spanning several pages) renders. Unconditional,
|
|
67
|
+
# not gated on change bars being enabled at all: Converter#
|
|
68
|
+
# convert_section's own revision-history dest needs this, pairing
|
|
69
|
+
# it with pdf-page-start (also captured at this same moment,
|
|
70
|
+
# upstream's own convert_section, right before the heading ink
|
|
71
|
+
# call this method wraps) so both fields describe the section's
|
|
72
|
+
# own start consistently. A page-spanning section's cursor read
|
|
73
|
+
# after the whole body renders belongs to wherever that body
|
|
74
|
+
# finished, not the section's own start page; two entries sharing
|
|
75
|
+
# the same start page then sort by a y value that was never on
|
|
76
|
+
# that page at all, reversing their true top-to-bottom order,
|
|
77
|
+
# confirmed directly against a real document.
|
|
78
|
+
def record_section_heading_position node
|
|
79
|
+
return unless node.context == :section
|
|
80
|
+
@section_heading_positions ||= {}
|
|
81
|
+
@section_heading_positions[node] = cursor
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def take_section_heading_position node
|
|
85
|
+
@section_heading_positions&.delete node
|
|
86
|
+
end
|
|
87
|
+
|
|
65
88
|
# A section's bar covers only its own directly-owned content, not any
|
|
66
89
|
# nested child sections/chapters: as soon as a child section begins
|
|
67
90
|
# rendering, close out the bar of every still-open ancestor section
|
|
@@ -177,11 +200,13 @@ module Asciidoctor
|
|
|
177
200
|
# have settled the page, so this is the first accurate start position.
|
|
178
201
|
def ink_general_heading node, title, opts = {}
|
|
179
202
|
record_change_bar_start node if @change_bar_attr && !scratch? && (change_bar? node)
|
|
203
|
+
record_section_heading_position node unless scratch?
|
|
180
204
|
super
|
|
181
205
|
end
|
|
182
206
|
|
|
183
207
|
def ink_chapter_title node, title, opts = {}
|
|
184
208
|
record_change_bar_start node if @change_bar_attr && !scratch? && (change_bar? node)
|
|
209
|
+
record_section_heading_position node unless scratch?
|
|
185
210
|
super
|
|
186
211
|
end
|
|
187
212
|
|
|
@@ -189,6 +214,7 @@ module Asciidoctor
|
|
|
189
214
|
# so it must be overridden separately
|
|
190
215
|
def ink_part_title node, title, opts = {}
|
|
191
216
|
record_change_bar_start node if @change_bar_attr && !scratch? && (change_bar? node)
|
|
217
|
+
record_section_heading_position node unless scratch?
|
|
192
218
|
super
|
|
193
219
|
end
|
|
194
220
|
end
|
|
@@ -227,6 +227,27 @@ module Asciidoctor
|
|
|
227
227
|
# page break. A pre-super page_number would be the page before that
|
|
228
228
|
# break, one page too low for any chapter-starting section.
|
|
229
229
|
page_num = ((node.attr 'pdf-page-start') || page_number).to_i
|
|
230
|
+
|
|
231
|
+
# y must describe the same physical location page_num does: the
|
|
232
|
+
# section's own start, not wherever cursor happens to sit once
|
|
233
|
+
# this whole call returns. A section's body can span several
|
|
234
|
+
# pages of its own (nested content, a large table, and so on);
|
|
235
|
+
# a post-super cursor read then belongs to whatever page that
|
|
236
|
+
# body finished on, not page_num's page, and the two stop
|
|
237
|
+
# describing the same point at all. take_section_heading_
|
|
238
|
+
# position (change_bars.rb) captured cursor at the same moment
|
|
239
|
+
# upstream captured pdf-page-start, right before the heading
|
|
240
|
+
# itself was inked, so pairing the two here always describes
|
|
241
|
+
# the section's own start consistently. Reported directly
|
|
242
|
+
# against a real document: two entries sharing the same
|
|
243
|
+
# page_sortable sorted in the wrong relative order, one of
|
|
244
|
+
# them a page-spanning section whose old post-super y actually
|
|
245
|
+
# belonged to the page after the one it was paired with.
|
|
246
|
+
# Falls back to the old post-super cursor only for a section
|
|
247
|
+
# with no heading ink call at all (a hidden title), no worse
|
|
248
|
+
# than before for that one remaining case.
|
|
249
|
+
y_pos = take_section_heading_position(node) || cursor
|
|
250
|
+
|
|
230
251
|
@anchor_catalog ||= {}
|
|
231
252
|
@anchor_catalog[node.id] ||= {
|
|
232
253
|
title: node.title,
|
|
@@ -235,10 +256,10 @@ module Asciidoctor
|
|
|
235
256
|
context: node.context,
|
|
236
257
|
level: node.level
|
|
237
258
|
}
|
|
238
|
-
@anchor_catalog[node.id][:dest] = { page: (rhrev_display_page page_num), physical_page: page_num, y:
|
|
259
|
+
@anchor_catalog[node.id][:dest] = { page: (rhrev_display_page page_num), physical_page: page_num, y: y_pos }
|
|
239
260
|
|
|
240
261
|
# Link destination to page for revision history
|
|
241
|
-
revision_history.link_dest_to_page node.id, page_num, (@rhrev_body_start_page || 1), y:
|
|
262
|
+
revision_history.link_dest_to_page node.id, page_num, (@rhrev_body_start_page || 1), y: y_pos
|
|
242
263
|
end
|
|
243
264
|
|
|
244
265
|
# Start position was captured at heading-ink time (after any page advance)
|
|
@@ -262,7 +283,7 @@ module Asciidoctor
|
|
|
262
283
|
# way, only the bar geometry was wrong, and bars are graphical.
|
|
263
284
|
def marked_table_cell_bar_rows node
|
|
264
285
|
marked_rows = []
|
|
265
|
-
(node.rows[:head] + node.rows[:body]).each_with_index do |row, row_idx|
|
|
286
|
+
(node.rows[:head] + node.rows[:body] + node.rows[:foot]).each_with_index do |row, row_idx|
|
|
266
287
|
marked_rows << row_idx if row.any? do |cell|
|
|
267
288
|
cell.respond_to?(:attributes) && cell.attributes && (cell.attributes.key? @change_bar_attr)
|
|
268
289
|
end
|
|
@@ -845,9 +866,16 @@ module Asciidoctor
|
|
|
845
866
|
# Must run before rollup_table_cell_revision_entries and before real
|
|
846
867
|
# rendering, so the cell's own attributes are already in place by
|
|
847
868
|
# the time either reads them.
|
|
869
|
+
#
|
|
870
|
+
# CellTextMarks.lift (cell_text_marks.rb) runs right before this: it
|
|
871
|
+
# reads the same attribute list off the cell's raw text, which is
|
|
872
|
+
# the only place it survives for a plain cell or any header-row
|
|
873
|
+
# cell, so a mark no longer needs the a| form at all. Both paths
|
|
874
|
+
# set the same attributes on the cell; this one still strips the
|
|
875
|
+
# nested block for the double-bar reason described here.
|
|
848
876
|
def propagate_cell_content_marks doc
|
|
849
877
|
doc.find_by(context: :table).each do |table|
|
|
850
|
-
cells = (table.rows[:head] + table.rows[:body]).flatten
|
|
878
|
+
cells = (table.rows[:head] + table.rows[:body] + table.rows[:foot]).flatten
|
|
851
879
|
cells.each do |cell|
|
|
852
880
|
next unless cell.style == :asciidoc
|
|
853
881
|
inner = (cell.inner_document rescue nil)
|
|
@@ -870,7 +898,7 @@ module Asciidoctor
|
|
|
870
898
|
|
|
871
899
|
def rollup_table_cell_revision_entries doc
|
|
872
900
|
doc.find_by(context: :table).each do |table|
|
|
873
|
-
cells = (table.rows[:head] + table.rows[:body]).flatten
|
|
901
|
+
cells = (table.rows[:head] + table.rows[:body] + table.rows[:foot]).flatten
|
|
874
902
|
cells.each do |cell|
|
|
875
903
|
attr_entries = (cell.instance_variable_get(:@attributes) rescue nil)
|
|
876
904
|
next if attr_entries.nil? || attr_entries.empty?
|
|
@@ -1099,6 +1127,7 @@ module Asciidoctor
|
|
|
1099
1127
|
end
|
|
1100
1128
|
end
|
|
1101
1129
|
|
|
1130
|
+
::Asciidoctor::Rhrev::CellTextMarks.lift doc, @revision_prefix
|
|
1102
1131
|
propagate_cell_content_marks doc
|
|
1103
1132
|
rollup_child_revision_entries doc
|
|
1104
1133
|
rollup_table_cell_revision_entries doc
|
|
@@ -31,13 +31,20 @@ module Asciidoctor
|
|
|
31
31
|
# as the PDF side: leaving it in place would tag both the cell and the
|
|
32
32
|
# nested paragraph independently, a redundant double border for what is
|
|
33
33
|
# really one mark on one piece of content.
|
|
34
|
+
#
|
|
35
|
+
# CellTextMarks.lift (cell_text_marks.rb) runs first inside this
|
|
36
|
+
# treeprocessor: it reads the same attribute list off the cell's raw
|
|
37
|
+
# text, which is the only place it survives for a plain cell or any
|
|
38
|
+
# header-row cell, so a mark no longer needs the a| form at all.
|
|
34
39
|
class TableCellMarkPropagationHtmlTreeprocessor < Asciidoctor::Extensions::Treeprocessor
|
|
35
40
|
def process document
|
|
36
41
|
return document unless document.basebackend? 'html'
|
|
37
42
|
prefix = document.attr 'revhistoryprefix', 'rhrev'
|
|
38
43
|
|
|
44
|
+
::Asciidoctor::Rhrev::CellTextMarks.lift document, prefix
|
|
45
|
+
|
|
39
46
|
document.find_by(context: :table).each do |table|
|
|
40
|
-
cells = (table.rows[:head] + table.rows[:body]).flatten
|
|
47
|
+
cells = (table.rows[:head] + table.rows[:body] + table.rows[:foot]).flatten
|
|
41
48
|
cells.each do |cell|
|
|
42
49
|
next unless cell.style == :asciidoc
|
|
43
50
|
inner = (cell.inner_document rescue nil)
|
|
@@ -469,7 +476,7 @@ module Asciidoctor
|
|
|
469
476
|
# treeprocessor phase, before this (a converter method) ever runs.
|
|
470
477
|
def rollup_table_cell_revision_entries catalog, doc, prefix
|
|
471
478
|
doc.find_by(context: :table).each do |table|
|
|
472
|
-
cells = (table.rows[:head] + table.rows[:body]).flatten
|
|
479
|
+
cells = (table.rows[:head] + table.rows[:body] + table.rows[:foot]).flatten
|
|
473
480
|
cells.each do |cell|
|
|
474
481
|
attr_entries = (cell.instance_variable_get(:@attributes) rescue nil)
|
|
475
482
|
next if attr_entries.nil? || attr_entries.empty?
|
data/lib/asciidoctor/rhrev.rb
CHANGED
|
@@ -6,6 +6,7 @@ require 'asciidoctor' unless defined? Asciidoctor
|
|
|
6
6
|
# Load shared components (processors, helpers, catalog)
|
|
7
7
|
require_relative 'rhrev/processors'
|
|
8
8
|
require_relative 'rhrev/helpers'
|
|
9
|
+
require_relative 'rhrev/cell_text_marks'
|
|
9
10
|
require_relative 'rhrev/catalog'
|
|
10
11
|
|
|
11
12
|
# Try to load the PDF converter support if asciidoctor-pdf is available
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asciidoctor-rhrev
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 白一百 baiyibai
|
|
@@ -38,6 +38,7 @@ files:
|
|
|
38
38
|
- lib/asciidoctor-rhrev.rb
|
|
39
39
|
- lib/asciidoctor/rhrev.rb
|
|
40
40
|
- lib/asciidoctor/rhrev/catalog.rb
|
|
41
|
+
- lib/asciidoctor/rhrev/cell_text_marks.rb
|
|
41
42
|
- lib/asciidoctor/rhrev/change_bars.rb
|
|
42
43
|
- lib/asciidoctor/rhrev/converter.rb
|
|
43
44
|
- lib/asciidoctor/rhrev/exporter.rb
|