asciidoctor-rhrev 1.5.3 → 1.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a4a09c2c018c9d8b79c1d819ff29867b1a398b683f22a4074392e0ac75c6ff4
4
- data.tar.gz: 7e7118f6e79d519b47380d1aaf52b9e9f8c22db573d66c45c13c9a2cc91aca74
3
+ metadata.gz: 7ff948e637bb99f9d64376d94ad293310988680a558ef3796aa797911210ad48
4
+ data.tar.gz: f47c7b2ecf6aa55d7e396b44221e8d7af6445c9c6085a71182e8b0a457e09226
5
5
  SHA512:
6
- metadata.gz: 4115b2013f9c56541ab3919cb8ca2f71e29167d00af6a105ede4da90d4e5e5bd96ab1ac86678ae1de7550929bd8939770ae52f7a4874f1822136117e0d359568
7
- data.tar.gz: 1f984b83343c2735c9fa7e7bd6bac3474eeabe6731a2237386d481a5f4d6f625f83368b4abf02e612bfa1898101b909237d0cb7a13286704894b8470153a2275
6
+ metadata.gz: a908055a506c6d32629d381a800c2c8911e649e0d4dfcd51ea2b5bb64a7d70e6bd4bb45229541947b9e840df95a9c3335d03179634cfe8efd99d51bedc8a5cb5
7
+ data.tar.gz: 8f5442540754978416479cf2b90f01d207993659b9e243277cdc1d645475dec294379691b62b88d1b7ad55ef3748f4af318486ea3bc7fc3ee6d6e08e2cf3d74b
data/CHANGELOG.adoc CHANGED
@@ -1,5 +1,18 @@
1
1
  = Changelog
2
2
 
3
+ == 1.7.0 (2026-09-02)
4
+
5
+ * 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
6
+ * Add per-item targeting instead, staying on the list's own existing attribute line: one or more trailing positional attributes name which item(s), by 1-based position, get the bar, `[rhrev1-2="Reworded step two",2]`. Several items and a dash range both work, mixed freely, `[rhrev1-2="Reworded the middle steps",2,4-6]`. No positional attribute at all keeps the exact behavior from every release before 1.6.0: one bar over the whole list. Confirmed directly before this design was picked, not assumed: a bare positional attribute cannot collide with anything AsciiDoc already claims on a list (style, and so on), since Asciidoctor tracks positional index by each entry's own place in the bracket, not by counting unnamed entries only, so a named `rhrev1-2="..."` occupying position 1 never contests a trailing bare position. The revision-history roll-up needs no new code at all: a targeted list is still just a marked list, already one of the existing roll-up contexts, already rolling up to its enclosing section correctly
7
+ * `Converter#convert_list_item` reads the innermost enclosing list's own target set off a stack `Converter#convert_targeted_list` maintains (the same shape `@change_bar_section_stack` already uses for nested sections), not off the list argument directly: for a `dlist` using the `unordered`, `ordered`, or `qanda` style, asciidoctor-pdf synthesizes a brand-new `List` object with no attributes of its own to render through, never the real, marked `dlist` node, so the stack is what carries the real target set down correctly regardless of which path produced the call. A `dlist` using the default or horizontal style has no per-item mechanism to target at all (asciidoctor-pdf renders those inline inside `convert_dlist` itself, no per-entry call to override, the same structural gap table-row-level marking has); a positional entry on one of those two styles falls back to marking the whole block, with a warning, rather than silently doing nothing
8
+ * Fix a bug caught during development, before this ever shipped: `bracket_change_bar`, the shared helper every other bar-inking call site already uses, gates on `change_bar? node`, checking the bracketed node's OWN attributes. A targeted item never carries the mark itself, only the enclosing list does, so reusing `bracket_change_bar` unmodified silently swallowed every targeted item's bar, real rendering showed no bar at all despite the target set correctly including the item's index. `Converter#ink_targeted_list_item_change_bar` is the fix, the same shape minus that gate, since membership in the already-resolved target set answers the "should this bar" question on its own. New regression file, `tests/regression/list-item-target-change-bars.rb`, includes a spy on `ink_change_bar` (the same technique `table-cell-change-bar.rb`'s own manual-mode pagerhref check already establishes) specifically because this exact bug would have passed every other check in the file: the revision-history roll-up is a separate mechanism from bar-inking and stayed correct the whole time the bar itself was silently missing
9
+ * Note: gem-only; the Antora extension (HTML output, no page-margin bar geometry the same way) is unaffected, npm still bumps concurrently per the standing 1.5.1 policy
10
+
11
+ == 1.6.0 (2026-09-02)
12
+
13
+ * Add per-item change bars and revision-history roll-up for individual items in an unordered or ordered list (PDF backend), previously only possible for a whole marked list. `Converter#convert_list_item` brackets asciidoctor-pdf's own per-item render call (`convert_list` calls it once per item directly, a real granular hook, unlike table rows) the same way the existing paragraph/ulist/olist/dlist overrides already bracket their own whole-block calls. No AsciiDoc syntax attaches a block attribute list directly to a list item's own bullet line, confirmed directly against a real parse: the attribute list is silently dropped, not attached to the item, the list, or anywhere else. The working construct, the same shape as an `a|` table cell's own workaround, is a block attribute list on content the item already legitimately attaches via a `+` continuation. `Converter#propagate_list_item_content_marks` copies a marked carrier block's rhrev attribute onto the enclosing item and strips it from the carrier, at prescan time, before real rendering: the strip matters because `convert_list_item`'s own `traverse_list_item` call renders an item's attached blocks through this same converter, so a left-marked carrier would independently trigger its own bar-inking hook too, a second bad bar. `list_item` is now one of the roll-up contexts, resolved via the same nearest-enclosing-section walk every other roll-up context already uses, reusing that mechanism unchanged. A marked sub-list attached to an item keeps its own independent mark; the new marker-carrier context set deliberately excludes ulist/olist/dlist/list_item so a compound item's own nested sub-list is never swallowed into the parent item's mark. A `dlist` using the `unordered`, `ordered`, or `qanda` style gets the same per-item support, since asciidoctor-pdf itself renders those through synthesized list items routed through the same hook; the default and `horizontal` `dlist` styles render each term and description pair inline with no per-item method call to override, the same category of structural gap table-row-level marking has, not attempted here. Verified directly: a real fixture rendered to PNG shows exactly one bar, spanning only the marked item, not the whole list and not bleeding into neighboring items; the revision-history table shows exactly one bullet for the marked item, nothing from its unmarked siblings. New regression file, `tests/regression/list-item-change-bars.rb`, confirmed to fail (a raised `NoMethodError`) without the fix and pass with it
14
+ * Note: gem-only; the Antora extension (HTML output, no page-margin bar geometry the same way) is unaffected, npm still bumps concurrently per the standing 1.5.1 policy
15
+
3
16
  == 1.5.3 (2026-09-01)
4
17
 
5
18
  * Fix `page_numbering_start_at: cover` still reporting the revision-history table and `pagerhref:` one page low when the theme has no real `front-cover-image`/`cover_front_image` of its own, for example a theme that draws its own cover through some other means. `Converter#compute_rhrev_page_numbering_start_page`'s `cover` branch fell back to plain `body_start` whenever `resolve_background_image` found nothing, matching 1.5.2's own `body`-shaped formula rather than the real one: asciidoctor-pdf itself falls back from `cover` to `title` in that case, not to `body`, and `title`'s own offset is 0 whenever the document has a title page at all (`doctype: book`, or the `title-page` attribute), regardless of whether a real front cover ever rendered. `body_start` only remains correct for the narrow remaining case of no front cover and no title page either. Reported directly against a real document: the table was one page low throughout. New regression case, `cover-no-image`, in `tests/regression/rhrev-page-numbering-start-at.rb`, confirmed to fail without the fix and pass with it
@@ -1,5 +1,6 @@
1
1
  require 'asciidoctor/pdf' unless defined? Asciidoctor::PDF
2
2
  require 'fileutils'
3
+ require 'set'
3
4
  require_relative 'helpers'
4
5
  require_relative 'catalog'
5
6
  require_relative 'renderer'
@@ -399,16 +400,151 @@ module Asciidoctor
399
400
  bracket_change_bar(node) { super }
400
401
  end
401
402
 
403
+ # A list marked with only the change-text attribute, [rhrev1-2="..."],
404
+ # is unchanged from every prior release: bracket_change_bar covers
405
+ # the whole list, one bar, one roll-up bullet.
406
+ #
407
+ # A list marked with one or more trailing positional attributes,
408
+ # [rhrev1-2="...",2] or [rhrev1-2="...",2,4-6], instead targets
409
+ # specific items by their 1-based position: no bar over the whole
410
+ # list, a separate bar for just the named item(s), inked by
411
+ # convert_list_item below. The roll-up bullet is unaffected either
412
+ # way, still the list's own single entry, [rhrev1-2="..."] is read
413
+ # exactly as before.
414
+ #
415
+ # A bare positional attribute cannot collide with a list's own
416
+ # style (ulist [square], olist numbering, and so on): confirmed
417
+ # directly, Asciidoctor tracks positional index by each entry's own
418
+ # place in the bracket, not by counting unnamed entries only, so a
419
+ # named rhrev1-2="..." occupying position 1 never contests the
420
+ # trailing bare position(s) after it.
402
421
  def convert_ulist node
403
- bracket_change_bar(node) { super }
422
+ convert_targeted_list(node) { super }
404
423
  end
405
424
 
406
425
  def convert_olist node
407
- bracket_change_bar(node) { super }
426
+ convert_targeted_list(node) { super }
408
427
  end
409
428
 
429
+ # dlist's unordered/ordered/qanda styles synthesize real ListItem
430
+ # objects and route them through convert_list_item the same as
431
+ # ulist/olist, confirmed directly against asciidoctor-pdf's own
432
+ # source (convert_dlist's own case statement, then convert_list),
433
+ # so per-item targeting reaches them too. The default and
434
+ # horizontal styles render every term/description pair inline
435
+ # inside convert_dlist itself, with no per-entry call to override
436
+ # at all, the same structural gap table-row-level marking has;
437
+ # per-item targets on one of those two styles fall back to marking
438
+ # the whole block instead, with a warning, rather than silently
439
+ # doing nothing.
440
+ DLIST_STYLES_ROUTING_THROUGH_LIST_ITEM = %w(unordered ordered qanda).freeze
441
+
410
442
  def convert_dlist node
411
- bracket_change_bar(node) { super }
443
+ targets = list_item_change_bar_targets node
444
+ if (::Set === targets) && !(DLIST_STYLES_ROUTING_THROUGH_LIST_ITEM.include? node.style)
445
+ log :warn, %(rhrev per-item list marking is not supported for a dlist using the '#{node.style || 'default'}' style; marking the whole block instead)
446
+ targets = :all
447
+ end
448
+ convert_targeted_list(node, targets) { super }
449
+ end
450
+
451
+ # Shared by convert_ulist/convert_olist/convert_dlist: computes the
452
+ # target set (unless the caller, convert_dlist, already resolved
453
+ # it), pushes it so convert_list_item can read the innermost
454
+ # enclosing list's own targets, and either brackets the whole list
455
+ # (targets == :all) or leaves bar-inking to convert_list_item
456
+ # entirely (targets is a real index Set, or nil, not marked at
457
+ # all). The stack, not a single ivar, matters for a list nested
458
+ # inside a marked item's own content: the same shape
459
+ # @change_bar_section_stack already uses for nested sections.
460
+ #
461
+ # Takes the real conversion as a block, not a bare super call
462
+ # inside this method: super here would look for
463
+ # Converter#convert_targeted_list up the ancestor chain, which
464
+ # does not exist, since this method's own name differs from the
465
+ # three real converter entry points calling it. The block, called
466
+ # from inside convert_ulist/convert_olist/convert_dlist
467
+ # themselves, keeps super resolving to the right upstream method,
468
+ # the same reason bracket_change_bar takes a block rather than
469
+ # trying to call super itself.
470
+ def convert_targeted_list node, targets = (list_item_change_bar_targets node)
471
+ @list_change_bar_target_stack ||= []
472
+ @list_change_bar_target_stack.push targets
473
+ if targets == :all
474
+ bracket_change_bar(node) { yield }
475
+ else
476
+ yield
477
+ end
478
+ ensure
479
+ @list_change_bar_target_stack.pop
480
+ end
481
+
482
+ # The target set for the list currently being converted: :all
483
+ # (bar the whole list, the only behavior before this feature
484
+ # existed), nil (not marked for the current revision at all), or a
485
+ # Set of 1-based item positions parsed from one or more trailing
486
+ # positional attributes, expanding an N-M token into a range.
487
+ # Reuses change_bar? for the marked/not-marked check, the same
488
+ # predicate every other bar-inking decision in this file already
489
+ # uses, so this only ever answers for the current revnumber.
490
+ def list_item_change_bar_targets node
491
+ return nil unless change_bar? node
492
+ raw = node.instance_variable_get(:@attributes)
493
+ positional_keys = raw.keys.select {|k| ::Integer === k }.sort
494
+ return :all if positional_keys.empty?
495
+ targets = ::Set.new
496
+ positional_keys.each do |key|
497
+ token = raw[key].to_s
498
+ if (m = /\A(\d+)-(\d+)\z/.match token)
499
+ (m[1].to_i..m[2].to_i).each {|i| targets << i }
500
+ elsif /\A\d+\z/.match? token
501
+ targets << token.to_i
502
+ end
503
+ end
504
+ targets.empty? ? :all : targets
505
+ end
506
+
507
+ # A single item within a ulist/olist (and a dlist using the
508
+ # unordered/ordered/qanda styles, real ListItem objects
509
+ # asciidoctor-pdf itself synthesizes and routes through here the
510
+ # same way). Reads the innermost enclosing list's own target set
511
+ # off the top of the stack convert_targeted_list maintains, not
512
+ # off list itself: for a dlist's synthesized styles, list here is
513
+ # a brand-new List object asciidoctor-pdf builds on the fly with
514
+ # no attributes of its own, never the real, marked dlist node, so
515
+ # reading list's own attributes directly would never see the
516
+ # mark. The stack is what carries the real target set down
517
+ # correctly regardless of which path produced this call.
518
+ #
519
+ # list.items.index node relies on plain object identity, correct
520
+ # here: asciidoctor-pdf's own convert_list passes each item
521
+ # straight from list.items into this method, no copying.
522
+ #
523
+ # Deliberately not bracket_change_bar: its own change_bar? node
524
+ # gate checks the item's OWN attributes, always false here, the
525
+ # mark lives on the enclosing list, never the item. That gate is
526
+ # exactly right for every other bracket_change_bar caller (the
527
+ # node being bracketed is the node that was actually marked); it
528
+ # would silently swallow every targeted item's bar here, caught
529
+ # directly by real rendering showing no bar at all despite the
530
+ # target set correctly including this item's index. ink_targeted_
531
+ # list_item_change_bar mirrors bracket_change_bar's own shape
532
+ # without that gate, since membership in targets already answers
533
+ # the "should this bar" question on its own.
534
+ def convert_list_item node, list, opts = {}
535
+ targets = @list_change_bar_target_stack&.last
536
+ if (::Set === targets) && (idx = list.items.index node) && (targets.include? idx + 1)
537
+ ink_targeted_list_item_change_bar { super }
538
+ else
539
+ super
540
+ end
541
+ end
542
+
543
+ def ink_targeted_list_item_change_bar
544
+ bar_from = { page: page_number, cursor: cursor } if @change_bar_attr && !scratch?
545
+ result = yield
546
+ ink_change_bar bar_from, { page: page_number, cursor: cursor } if bar_from
547
+ result
412
548
  end
413
549
 
414
550
  def convert_rhrev node
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.5.3
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 白一百 baiyibai