canon 0.3.48 → 0.3.50
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/lib/canon/diff/diff_node_enricher.rb +37 -15
- data/lib/canon/diff_formatter/by_line/xml_formatter.rb +43 -0
- data/lib/canon/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6bbfa0c65294f7a3ad7d77b678dab6fc1b9f5c0fc67bf55faba3371e6cfc6614
|
|
4
|
+
data.tar.gz: 28b3106753c227ad6973a2698ede0315d3e8812feed6af1cbccdf5cf08636184
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d3c4a330ec9224eff5a8c84bd677ea1fc72500b06099c188b3c7b30e6b096d3897f8308a40fc003067887dd4f45986464c8786b8bee8096614035dc8189c8ea
|
|
7
|
+
data.tar.gz: 84549e0eb08a383bf14934b8b22f58e3ad4aeac8aaaeb56ce80c87d1127df9d3ebc7d2f17763cf6cfd1dda65702e90d2379e0ba5d64193f250bd1f409d292aa3
|
|
@@ -41,6 +41,11 @@ module Canon
|
|
|
41
41
|
# Track occurrences for text_content dimension to find correct element instance
|
|
42
42
|
@text_occurrence1 = Hash.new(0)
|
|
43
43
|
@text_occurrence2 = Hash.new(0)
|
|
44
|
+
# Opener-offset caches: one scan per unique element name per
|
|
45
|
+
# document replaces the per-call regex walk (which was also
|
|
46
|
+
# quadratic on repeated element names).
|
|
47
|
+
@opener_offsets1 = {}
|
|
48
|
+
@opener_offsets2 = {}
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
def enrich
|
|
@@ -1461,27 +1466,44 @@ range_start, range_end)
|
|
|
1461
1466
|
# tag (no inside-the-element correction — see
|
|
1462
1467
|
# locate_element_at_index).
|
|
1463
1468
|
def count_elements_before_position_open(text, char_offset, opener)
|
|
1464
|
-
|
|
1465
|
-
pos = 0
|
|
1466
|
-
while (hit = text.index(opener, pos)) && hit < char_offset
|
|
1467
|
-
count += 1
|
|
1468
|
-
pos = hit + 1
|
|
1469
|
-
end
|
|
1470
|
-
count
|
|
1469
|
+
count_openers_before(opener_offsets(text, opener), char_offset)
|
|
1471
1470
|
end
|
|
1472
1471
|
|
|
1473
1472
|
def count_elements_before_position(text, char_offset, element_name)
|
|
1474
|
-
#
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1473
|
+
# Subtract 1 because the count includes the element we are inside
|
|
1474
|
+
[count_openers_before(opener_offsets(text, opener_for(element_name)),
|
|
1475
|
+
char_offset) - 1, 0].max
|
|
1476
|
+
end
|
|
1477
|
+
|
|
1478
|
+
# Count of opener offsets strictly before char_offset — binary
|
|
1479
|
+
# search over the cached, ascending offset array.
|
|
1480
|
+
def count_openers_before(offsets, char_offset)
|
|
1481
|
+
idx = offsets.bsearch_index { |offset| offset >= char_offset }
|
|
1482
|
+
idx || offsets.length
|
|
1483
|
+
end
|
|
1484
|
+
|
|
1485
|
+
# All opener offsets for one opener regex in one document,
|
|
1486
|
+
# cached per text (one linear scan per unique element name per
|
|
1487
|
+
# document — callers previously re-scanned per occurrence
|
|
1488
|
+
# check, which was also quadratic on repeated element names).
|
|
1489
|
+
def opener_offsets(text, opener)
|
|
1490
|
+
if text.equal?(@text1)
|
|
1491
|
+
(@opener_offsets1[opener.source] ||= scan_opener_offsets(text, opener))
|
|
1492
|
+
elsif text.equal?(@text2)
|
|
1493
|
+
(@opener_offsets2[opener.source] ||= scan_opener_offsets(text, opener))
|
|
1494
|
+
else
|
|
1495
|
+
scan_opener_offsets(text, opener)
|
|
1496
|
+
end
|
|
1497
|
+
end
|
|
1498
|
+
|
|
1499
|
+
def scan_opener_offsets(text, opener)
|
|
1500
|
+
offsets = []
|
|
1478
1501
|
pos = 0
|
|
1479
|
-
while (hit = text.index(opener, pos))
|
|
1480
|
-
|
|
1502
|
+
while (hit = text.index(opener, pos))
|
|
1503
|
+
offsets << hit
|
|
1481
1504
|
pos = hit + 1
|
|
1482
1505
|
end
|
|
1483
|
-
|
|
1484
|
-
[count - 1, 0].max
|
|
1506
|
+
offsets
|
|
1485
1507
|
end
|
|
1486
1508
|
end
|
|
1487
1509
|
end
|
|
@@ -118,6 +118,13 @@ module Canon
|
|
|
118
118
|
after = diff_node.serialized_after&.split("\n") || []
|
|
119
119
|
fmt = diff_node.formatting?
|
|
120
120
|
lines = []
|
|
121
|
+
header = parent_chain(diff_node.path)
|
|
122
|
+
if header
|
|
123
|
+
lines << Canon::Diff::DiffLine.new(
|
|
124
|
+
line_number: nil, new_position: nil,
|
|
125
|
+
content: header, type: :location_header
|
|
126
|
+
)
|
|
127
|
+
end
|
|
121
128
|
require "diff/lcs" unless defined?(::Diff::LCS)
|
|
122
129
|
|
|
123
130
|
::Diff::LCS.sdiff(before, after).each do |change|
|
|
@@ -156,6 +163,27 @@ module Canon
|
|
|
156
163
|
lines
|
|
157
164
|
end
|
|
158
165
|
|
|
166
|
+
# Display chain of a DiffNode's ancestor elements, e.g.
|
|
167
|
+
# "bibitem > formattedref" — gives the blank-numbered
|
|
168
|
+
# unlocated lines a readable home (#86).
|
|
169
|
+
def parent_chain(path)
|
|
170
|
+
return nil if path.nil?
|
|
171
|
+
|
|
172
|
+
segments = path.split("/").reject(&:empty?)
|
|
173
|
+
return nil if segments.length < 2
|
|
174
|
+
|
|
175
|
+
chain = segments[0...-1].filter_map do |seg|
|
|
176
|
+
seg[/\A(?:\{[^}]+\})?([a-zA-Z0-9_:-]+)/, 1]
|
|
177
|
+
end
|
|
178
|
+
# Comparator paths repeat the element name before the node
|
|
179
|
+
# segment ("formattedref/formattedref/text()") — collapse
|
|
180
|
+
# consecutive repeats for display.
|
|
181
|
+
collapsed = chain.each_with_object([]) do |name, acc|
|
|
182
|
+
acc << name unless acc.last == name
|
|
183
|
+
end
|
|
184
|
+
collapsed.join(" > ")
|
|
185
|
+
end
|
|
186
|
+
|
|
159
187
|
# The nearest preceding node (in @differences walk order)
|
|
160
188
|
# that the enricher DID locate.
|
|
161
189
|
def preceding_located(diff_node)
|
|
@@ -225,6 +253,9 @@ module Canon
|
|
|
225
253
|
new_num = (diff_line.new_position || diff_line.line_number)&.+(1)
|
|
226
254
|
output << format_unified_line(old_num, new_num, " ",
|
|
227
255
|
diff_line.content)
|
|
256
|
+
when :location_header
|
|
257
|
+
output << format_location_header(diff_line.content)
|
|
258
|
+
|
|
228
259
|
when :removed
|
|
229
260
|
line_num = diff_line.line_number&.+(1)
|
|
230
261
|
formatting = diff_line.formatting?
|
|
@@ -992,6 +1023,18 @@ module Canon
|
|
|
992
1023
|
end
|
|
993
1024
|
end
|
|
994
1025
|
|
|
1026
|
+
# Dimmed section header for unlocated-node contexts (#86):
|
|
1027
|
+
# "⋯ in bibitem > formattedref"
|
|
1028
|
+
def format_location_header(chain)
|
|
1029
|
+
blank = " " * @line_num_width
|
|
1030
|
+
text = " ⋯ in #{chain}"
|
|
1031
|
+
if @use_color
|
|
1032
|
+
"#{blank}|#{blank} | #{colorize(text, :light_black)}"
|
|
1033
|
+
else
|
|
1034
|
+
"#{blank}|#{blank} | #{text}"
|
|
1035
|
+
end
|
|
1036
|
+
end
|
|
1037
|
+
|
|
995
1038
|
def format_unified_line(old_num, new_num, marker, content, color = nil,
|
|
996
1039
|
informative: false, formatting: false)
|
|
997
1040
|
old_str = old_num ? "%#{@line_num_width}d" % old_num : " " * @line_num_width
|
data/lib/canon/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: canon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.50
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: diff-lcs
|