porrima 0.2.0 → 0.3.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: 12e94d3fff8b491f17f5cb13beb41d396496816c54995c16be69b3aebdba875f
4
- data.tar.gz: df24fab43b8358f2fcc522b548a1ead14ef53faf365a6601d0bc10c3a798aeed
3
+ metadata.gz: 9cb36f8d5f52c2bd1c4ec9b43223de3fea75b17b4059402ce5659fc14bc5fe9e
4
+ data.tar.gz: 68e095935908555e20d8f76f4a4eaa559a7ebd909be5c72d074e6bce9ab84f45
5
5
  SHA512:
6
- metadata.gz: 24a68e804a09990a553b353e9ca2c547f64ca72cb382a121212ee1222f44b75d17b0ed07ca499a4200539279d245484723e602b43a3bb1972c51cbd86251b81c
7
- data.tar.gz: b61f20f0ea9d25c231e30b1efd01a91152f7e3e76bd69edd3dec00c634c27123fcb15c9d2e4c66a33f35a2207d0b0c72d800cc2c605578ca42b15823a975824c
6
+ metadata.gz: 8ec0cc6a0a47b1cd2570d33377fee475a3c45e3f787d67e598ef19bedace1a99624fc4ad1e7771f7fe338c6fa6a82092db7d76304155d4069209f9a344b387f2
7
+ data.tar.gz: e7a8308c5d1829640d1db2fb6fee7497140201968cc7e0ebcab5e066bd657ce1f512e5fa9d76bfdf059919d8142879665fad76d1407b8d47d4d5ddd4af7fa212
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 — 2026-09-24
4
+
5
+ - Add sparse cell and ordered slide difference APIs without application dependencies.
6
+
7
+ ## 0.2.1 — 2026-09-21
8
+
9
+ - Add a deterministic side-by-side diff demo image and regeneration task.
10
+
3
11
  ## 0.2.0 — 2026-09-15
4
12
 
5
13
  - Add zero-based merge output regions for conflict marker blocks.
data/README.md CHANGED
@@ -26,12 +26,15 @@ Porrima turns two or three texts into structured edits, hunks, patches, and
26
26
  merge conflicts. Its core performs no I/O, knows nothing about Git, and leaves
27
27
  color, width, truncation, caching, and other display policy to the caller.
28
28
 
29
+ ![Porrima side-by-side diff](docs/media/screenshot.png)
30
+
29
31
  ## Features
30
32
 
31
33
  - Linear-space Myers diff for lines and inline word or character refinement
32
34
  - Context hunks, change statistics, gutter marks, and paired rows
33
35
  - Unified diff output with strict patch parsing, application, and reversal
34
36
  - Three-way merge with structured conflicts, output regions, and immutable resolution
37
+ - Sparse sheet cell changes and ordered slide changes without application dependencies
35
38
  - CLI output as unified text or JSON
36
39
  - No runtime dependencies
37
40
 
@@ -71,6 +74,31 @@ puts diff.to_unified(old_name: "a/example", new_name: "b/example")
71
74
  | `Porrima::Inline.refine(before, after)` | Word- or character-level spans |
72
75
  | `Porrima::Patch.parse(text)` | Parsed unified file diffs |
73
76
  | `Porrima::Merge.three_way(...)` | Structured three-way merge result |
77
+ | `Porrima::Structured.sheet(before, after)` | Changed cells in row/column order |
78
+ | `Porrima::Structured.slides(before, after)` | Added, removed, or modified slide positions |
79
+
80
+ ### Structured data
81
+
82
+ `Structured.sheet` accepts sparse sheets with `row_count`, `column_count`, and
83
+ `each_in(top, left, bottom, right)`, such as `Denebola::Sheet` returned by
84
+ `Rukbat::Workbook#sheet`. It returns only changed cells, with zero-based
85
+ `row` and `column`, `kind`, `before`, and `after`. A missing cell is `nil`.
86
+
87
+ `Structured.slides` accepts two ordered arrays. Pass a block to compare a
88
+ projection of each slide, while the results retain the original slide values:
89
+
90
+ ```ruby
91
+ before = old_deck.slides
92
+ after = new_deck.slides
93
+ changes = Porrima::Structured.slides(before, after) do |slide|
94
+ [slide.layout, slide.slots.transform_values(&:text), slide.notes]
95
+ end
96
+ ```
97
+
98
+ Each change has `kind`, zero-based `old_index`/`new_index`, and `before`/`after`.
99
+ An absent side has a `nil` index and value. Choose a projection that includes
100
+ everything relevant to your comparison; for exact Markdown changes, compare
101
+ the decks' source text with `Porrima.diff`.
74
102
 
75
103
  ### Diff result
76
104
 
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porrima
4
+ module Structured
5
+ CellChange = Struct.new(:kind, :row, :column, :before, :after, keyword_init: true)
6
+ SlideChange = Struct.new(:kind, :old_index, :new_index, :before, :after, keyword_init: true)
7
+
8
+ module_function
9
+
10
+ # Coordinates are zero-based, matching the sparse sheet's each_in points.
11
+ def sheet(before, after)
12
+ old_cells = cells(before)
13
+ new_cells = cells(after)
14
+ (old_cells.keys | new_cells.keys).sort.filter_map do |row, column|
15
+ position = [row, column]
16
+ old_present = old_cells.key?(position)
17
+ new_present = new_cells.key?(position)
18
+ old_value = old_cells[position]
19
+ new_value = new_cells[position]
20
+ next if old_present && new_present && old_value == new_value
21
+
22
+ kind = old_present ? (new_present ? :modified : :removed) : :added
23
+ CellChange.new(kind: kind, row: row, column: column, before: old_value, after: new_value).freeze
24
+ end.freeze
25
+ end
26
+
27
+ # The block selects the content used to align slides. Without it, values
28
+ # themselves are compared. Positions are zero-based, matching Deck#slide.
29
+ def slides(before, after, &identity)
30
+ identity ||= ->(slide) { slide }
31
+ ids = {}
32
+ old_ids = slide_ids(before, ids, identity)
33
+ new_ids = slide_ids(after, ids, identity)
34
+ Porrima.diff(old_ids, new_ids).rows.filter_map do |row|
35
+ next if row.kind == :equal
36
+
37
+ old_index = row.old_line && row.old_line - 1
38
+ new_index = row.new_line && row.new_line - 1
39
+ SlideChange.new(kind: row.kind, old_index: old_index, new_index: new_index,
40
+ before: old_index && before[old_index], after: new_index && after[new_index]).freeze
41
+ end.freeze
42
+ end
43
+
44
+ def cells(sheet)
45
+ entries = {}
46
+ return entries if sheet.row_count.zero? || sheet.column_count.zero?
47
+
48
+ sheet.each_in(0, 0, sheet.row_count - 1, sheet.column_count - 1) do |point, value|
49
+ entries[[point.row, point.column]] = value
50
+ end
51
+ entries
52
+ end
53
+ private_class_method :cells
54
+
55
+ def slide_ids(slides, ids, identity)
56
+ slides.map { |slide| ids[identity.call(slide)] ||= ids.length.to_s }
57
+ end
58
+ private_class_method :slide_ids
59
+ end
60
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Porrima
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/porrima.rb CHANGED
@@ -11,6 +11,7 @@ require_relative "porrima/diff"
11
11
  require_relative "porrima/patch"
12
12
  require_relative "porrima/inline"
13
13
  require_relative "porrima/merge"
14
+ require_relative "porrima/structured"
14
15
 
15
16
  module Porrima
16
17
  module_function
data/sig/porrima.rbs CHANGED
@@ -134,6 +134,27 @@ module Porrima
134
134
  def self.conflict_inline: (Conflict) -> { ours: Array[Span], theirs: Array[Span] }
135
135
  end
136
136
 
137
+ module Structured
138
+ class CellChange < Struct[untyped]
139
+ attr_accessor kind: :added | :modified | :removed
140
+ attr_accessor row: Integer
141
+ attr_accessor column: Integer
142
+ attr_accessor before: untyped
143
+ attr_accessor after: untyped
144
+ def self.new: (kind: (:added | :modified | :removed), row: Integer, column: Integer, before: untyped, after: untyped) -> instance
145
+ end
146
+ class SlideChange < Struct[untyped]
147
+ attr_accessor kind: :added | :modified | :removed
148
+ attr_accessor old_index: Integer?
149
+ attr_accessor new_index: Integer?
150
+ attr_accessor before: untyped
151
+ attr_accessor after: untyped
152
+ def self.new: (kind: (:added | :modified | :removed), old_index: Integer?, new_index: Integer?, before: untyped, after: untyped) -> instance
153
+ end
154
+ def self.sheet: (untyped before, untyped after) -> Array[CellChange]
155
+ def self.slides: (Array[untyped] before, Array[untyped] after) ?{ (untyped) -> untyped } -> Array[SlideChange]
156
+ end
157
+
137
158
  def self.diff: (lines, lines, ?context: Integer, ?budget: Budget?) -> Diff
138
159
  def self.edits: (lines, lines, ?budget: Budget?) -> Array[Edit]
139
160
  def self.hunks: (lines, lines, ?context: Integer, ?budget: Budget?) -> Array[Hunk]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: porrima
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yudai Takada
@@ -34,6 +34,7 @@ files:
34
34
  - lib/porrima/merge.rb
35
35
  - lib/porrima/myers.rb
36
36
  - lib/porrima/patch.rb
37
+ - lib/porrima/structured.rb
37
38
  - lib/porrima/unified.rb
38
39
  - lib/porrima/version.rb
39
40
  - sig/porrima.rbs