prosereflect 0.1.1 → 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 +4 -4
- data/.github/workflows/docs.yml +63 -0
- data/.github/workflows/links.yml +97 -0
- data/.github/workflows/rake.yml +4 -0
- data/.github/workflows/release.yml +5 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +19 -1
- data/.rubocop_todo.yml +119 -183
- data/CLAUDE.md +78 -0
- data/Gemfile +8 -4
- data/README.adoc +2 -0
- data/Rakefile +3 -3
- data/docs/Gemfile +10 -0
- data/docs/INDEX.adoc +45 -0
- data/docs/_advanced/index.adoc +15 -0
- data/docs/_advanced/schema.adoc +112 -0
- data/docs/_advanced/step-map.adoc +66 -0
- data/docs/_advanced/steps.adoc +88 -0
- data/docs/_advanced/test-builder.adoc +61 -0
- data/docs/_advanced/transform.adoc +92 -0
- data/docs/_config.yml +174 -0
- data/docs/_features/html-input.adoc +69 -0
- data/docs/_features/html-output.adoc +45 -0
- data/docs/_features/index.adoc +15 -0
- data/docs/_features/marks.adoc +86 -0
- data/docs/_features/node-types.adoc +124 -0
- data/docs/_features/user-mentions.adoc +47 -0
- data/docs/_guides/custom-nodes.adoc +107 -0
- data/docs/_guides/index.adoc +13 -0
- data/docs/_guides/round-trip-html.adoc +91 -0
- data/docs/_guides/serialization.adoc +109 -0
- data/docs/_pages/index.adoc +67 -0
- data/docs/_reference/document-api.adoc +49 -0
- data/docs/_reference/index.adoc +14 -0
- data/docs/_reference/node-api.adoc +79 -0
- data/docs/_reference/schema-api.adoc +95 -0
- data/docs/_reference/transform-api.adoc +77 -0
- data/docs/_understanding/document-model.adoc +65 -0
- data/docs/_understanding/fragment.adoc +52 -0
- data/docs/_understanding/index.adoc +14 -0
- data/docs/_understanding/resolved-position.adoc +53 -0
- data/docs/_understanding/slice.adoc +54 -0
- data/docs/lychee.toml +63 -0
- data/lib/prosereflect/attribute/base.rb +4 -6
- data/lib/prosereflect/attribute/bold.rb +2 -4
- data/lib/prosereflect/attribute/href.rb +1 -3
- data/lib/prosereflect/attribute/id.rb +7 -7
- data/lib/prosereflect/attribute.rb +4 -7
- data/lib/prosereflect/blockquote.rb +19 -11
- data/lib/prosereflect/bullet_list.rb +36 -29
- data/lib/prosereflect/code_block.rb +23 -27
- data/lib/prosereflect/code_block_wrapper.rb +12 -13
- data/lib/prosereflect/document.rb +14 -22
- data/lib/prosereflect/fragment.rb +249 -0
- data/lib/prosereflect/hard_break.rb +6 -6
- data/lib/prosereflect/heading.rb +14 -15
- data/lib/prosereflect/horizontal_rule.rb +23 -14
- data/lib/prosereflect/image.rb +32 -23
- data/lib/prosereflect/input/html.rb +179 -104
- data/lib/prosereflect/input.rb +7 -0
- data/lib/prosereflect/list_item.rb +11 -12
- data/lib/prosereflect/mark/base.rb +9 -11
- data/lib/prosereflect/mark/bold.rb +1 -3
- data/lib/prosereflect/mark/code.rb +1 -3
- data/lib/prosereflect/mark/italic.rb +1 -3
- data/lib/prosereflect/mark/link.rb +1 -3
- data/lib/prosereflect/mark/strike.rb +1 -3
- data/lib/prosereflect/mark/subscript.rb +1 -3
- data/lib/prosereflect/mark/superscript.rb +1 -3
- data/lib/prosereflect/mark/underline.rb +1 -3
- data/lib/prosereflect/mark.rb +9 -5
- data/lib/prosereflect/node.rb +171 -33
- data/lib/prosereflect/ordered_list.rb +17 -14
- data/lib/prosereflect/output/html.rb +279 -50
- data/lib/prosereflect/output.rb +7 -0
- data/lib/prosereflect/paragraph.rb +11 -13
- data/lib/prosereflect/parser.rb +56 -66
- data/lib/prosereflect/resolved_pos.rb +256 -0
- data/lib/prosereflect/schema/attribute.rb +57 -0
- data/lib/prosereflect/schema/content_match.rb +656 -0
- data/lib/prosereflect/schema/fragment.rb +166 -0
- data/lib/prosereflect/schema/mark.rb +121 -0
- data/lib/prosereflect/schema/mark_type.rb +130 -0
- data/lib/prosereflect/schema/node.rb +236 -0
- data/lib/prosereflect/schema/node_type.rb +274 -0
- data/lib/prosereflect/schema/schema_main.rb +190 -0
- data/lib/prosereflect/schema/spec.rb +92 -0
- data/lib/prosereflect/schema.rb +39 -0
- data/lib/prosereflect/table.rb +12 -13
- data/lib/prosereflect/table_cell.rb +13 -13
- data/lib/prosereflect/table_header.rb +17 -17
- data/lib/prosereflect/table_row.rb +12 -12
- data/lib/prosereflect/text.rb +35 -11
- data/lib/prosereflect/transform/attr_step.rb +157 -0
- data/lib/prosereflect/transform/insert_step.rb +115 -0
- data/lib/prosereflect/transform/mapping.rb +82 -0
- data/lib/prosereflect/transform/mark_step.rb +269 -0
- data/lib/prosereflect/transform/replace_around_step.rb +181 -0
- data/lib/prosereflect/transform/replace_step.rb +157 -0
- data/lib/prosereflect/transform/slice.rb +91 -0
- data/lib/prosereflect/transform/step.rb +89 -0
- data/lib/prosereflect/transform/step_map.rb +126 -0
- data/lib/prosereflect/transform/structure.rb +120 -0
- data/lib/prosereflect/transform/transform.rb +341 -0
- data/lib/prosereflect/transform.rb +26 -0
- data/lib/prosereflect/user.rb +15 -15
- data/lib/prosereflect/version.rb +1 -1
- data/lib/prosereflect.rb +30 -17
- data/prosereflect.gemspec +17 -16
- data/spec/fixtures/documents/formatted_text.yaml +14 -0
- data/spec/fixtures/documents/heading_paragraph.yaml +16 -0
- data/spec/fixtures/documents/lists_doc.yaml +32 -0
- data/spec/fixtures/documents/mixed_content.yaml +40 -0
- data/spec/fixtures/documents/nested_doc.yaml +20 -0
- data/spec/fixtures/documents/simple_doc.yaml +6 -0
- data/spec/fixtures/documents/table_doc.yaml +32 -0
- data/spec/fixtures/documents/transform_test.yaml +14 -0
- data/spec/fixtures/schema/custom_schema.rb +37 -0
- data/spec/fixtures/schema/test_schema.rb +46 -0
- data/spec/fixtures/test_builder/helpers.rb +212 -0
- data/spec/prosereflect/document_spec.rb +332 -330
- data/spec/prosereflect/fragment_spec.rb +273 -0
- data/spec/prosereflect/hard_break_spec.rb +125 -125
- data/spec/prosereflect/input/html_spec.rb +718 -522
- data/spec/prosereflect/node_spec.rb +311 -182
- data/spec/prosereflect/output/html_spec.rb +105 -105
- data/spec/prosereflect/output/whitespace_spec.rb +248 -0
- data/spec/prosereflect/paragraph_spec.rb +275 -274
- data/spec/prosereflect/parser/round_trip_spec.rb +472 -0
- data/spec/prosereflect/parser_spec.rb +185 -180
- data/spec/prosereflect/resolved_pos_spec.rb +74 -0
- data/spec/prosereflect/schema/conftest.rb +68 -0
- data/spec/prosereflect/schema/content_match_spec.rb +237 -0
- data/spec/prosereflect/schema/mark_spec.rb +274 -0
- data/spec/prosereflect/schema/mark_type_spec.rb +86 -0
- data/spec/prosereflect/schema/node_type_spec.rb +142 -0
- data/spec/prosereflect/schema/schema_spec.rb +194 -0
- data/spec/prosereflect/table_cell_spec.rb +183 -183
- data/spec/prosereflect/table_row_spec.rb +149 -149
- data/spec/prosereflect/table_spec.rb +320 -318
- data/spec/prosereflect/test_builder/marks_spec.rb +127 -0
- data/spec/prosereflect/text_spec.rb +133 -132
- data/spec/prosereflect/transform/equivalence_spec.rb +487 -0
- data/spec/prosereflect/transform/mapping_spec.rb +226 -0
- data/spec/prosereflect/transform/replace_spec.rb +832 -0
- data/spec/prosereflect/transform/replace_step_spec.rb +157 -0
- data/spec/prosereflect/transform/slice_spec.rb +48 -0
- data/spec/prosereflect/transform/step_map_spec.rb +70 -0
- data/spec/prosereflect/transform/step_spec.rb +211 -0
- data/spec/prosereflect/transform/structure_spec.rb +98 -0
- data/spec/prosereflect/transform/transform_spec.rb +238 -0
- data/spec/prosereflect/user_spec.rb +31 -28
- data/spec/prosereflect_spec.rb +28 -26
- data/spec/spec_helper.rb +7 -6
- data/spec/support/matchers.rb +6 -6
- data/spec/support/shared_examples.rb +49 -49
- metadata +96 -5
- data/spec/prosereflect/version_spec.rb +0 -11
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "prosereflect/output/html"
|
|
5
|
+
|
|
6
|
+
RSpec.describe Prosereflect::Output::DOMSerializer do # rubocop:disable RSpec/SpecFilePathFormat
|
|
7
|
+
let(:serializer) { described_class.new(nil) }
|
|
8
|
+
|
|
9
|
+
describe "#preserve_whitespace?" do
|
|
10
|
+
it "returns true for code_block nodes" do
|
|
11
|
+
code_block = Prosereflect::CodeBlock.new
|
|
12
|
+
expect(serializer.send(:preserve_whitespace?, code_block)).to be true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "returns true for code_block_wrapper nodes" do
|
|
16
|
+
wrapper = Prosereflect::CodeBlockWrapper.new
|
|
17
|
+
expect(serializer.send(:preserve_whitespace?, wrapper)).to be true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "returns true for pre type nodes" do
|
|
21
|
+
node = Prosereflect::Node.new("pre")
|
|
22
|
+
expect(serializer.send(:preserve_whitespace?, node)).to be true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "returns true for nodes with white-space: pre in style attrs" do
|
|
26
|
+
node = Prosereflect::Node.new("paragraph")
|
|
27
|
+
node.attrs = { "style" => "white-space: pre; color: red" }
|
|
28
|
+
expect(serializer.send(:preserve_whitespace?, node)).to be true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "returns false for paragraph nodes" do
|
|
32
|
+
paragraph = Prosereflect::Paragraph.new
|
|
33
|
+
expect(serializer.send(:preserve_whitespace?, paragraph)).to be false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "returns false for heading nodes" do
|
|
37
|
+
heading = Prosereflect::Heading.new
|
|
38
|
+
heading.level = 1
|
|
39
|
+
expect(serializer.send(:preserve_whitespace?, heading)).to be false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "returns false for nodes without white-space: pre style" do
|
|
43
|
+
node = Prosereflect::Node.new("paragraph")
|
|
44
|
+
node.attrs = { "style" => "color: red" }
|
|
45
|
+
expect(serializer.send(:preserve_whitespace?, node)).to be false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "returns false for nodes with non-string style attrs" do
|
|
49
|
+
node = Prosereflect::Node.new("paragraph")
|
|
50
|
+
node.attrs = { "style" => 123 }
|
|
51
|
+
expect(serializer.send(:preserve_whitespace?, node)).to be false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "returns false when node does not respond to type" do
|
|
55
|
+
expect(serializer.send(:preserve_whitespace?, "not a node")).to be false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "returns false when node does not respond to attrs" do
|
|
59
|
+
node = Prosereflect::Node.new("text")
|
|
60
|
+
allow(node).to receive(:respond_to?).and_call_original
|
|
61
|
+
expect(serializer.send(:preserve_whitespace?, node)).to be false
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe "#whitespace_mode" do
|
|
66
|
+
it "returns :preserve for code_block nodes" do
|
|
67
|
+
code_block = Prosereflect::CodeBlock.new
|
|
68
|
+
expect(serializer.send(:whitespace_mode, code_block)).to eq(:preserve)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "returns :preserve for code_block_wrapper nodes" do
|
|
72
|
+
wrapper = Prosereflect::CodeBlockWrapper.new
|
|
73
|
+
expect(serializer.send(:whitespace_mode, wrapper)).to eq(:preserve)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "returns :collapse for paragraph nodes" do
|
|
77
|
+
paragraph = Prosereflect::Paragraph.new
|
|
78
|
+
expect(serializer.send(:whitespace_mode, paragraph)).to eq(:collapse)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "returns :collapse for heading nodes" do
|
|
82
|
+
heading = Prosereflect::Heading.new
|
|
83
|
+
heading.level = 2
|
|
84
|
+
expect(serializer.send(:whitespace_mode, heading)).to eq(:collapse)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "returns :preserve for nodes with white-space: pre style" do
|
|
88
|
+
node = Prosereflect::Node.new("paragraph")
|
|
89
|
+
node.attrs = { "style" => "white-space: pre" }
|
|
90
|
+
expect(serializer.send(:whitespace_mode, node)).to eq(:preserve)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe "#collapse_whitespace" do
|
|
95
|
+
it "collapses multiple spaces into one" do
|
|
96
|
+
expect(serializer.send(:collapse_whitespace, "hello world")).to eq("hello world")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "collapses tabs into a single space" do
|
|
100
|
+
expect(serializer.send(:collapse_whitespace, "hello\tworld")).to eq("hello world")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "collapses mixed tabs and spaces into a single space" do
|
|
104
|
+
expect(serializer.send(:collapse_whitespace, "hello \t world")).to eq("hello world")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "collapses leading spaces" do
|
|
108
|
+
expect(serializer.send(:collapse_whitespace, " hello")).to eq(" hello")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "collapses trailing spaces" do
|
|
112
|
+
expect(serializer.send(:collapse_whitespace, "hello ")).to eq("hello ")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "collapses both leading and trailing spaces" do
|
|
116
|
+
expect(serializer.send(:collapse_whitespace, " hello world ")).to eq(" hello world ")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "handles a single space string" do
|
|
120
|
+
expect(serializer.send(:collapse_whitespace, " ")).to eq(" ")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "handles an empty string" do
|
|
124
|
+
expect(serializer.send(:collapse_whitespace, "")).to eq("")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "does not modify a string with no extra whitespace" do
|
|
128
|
+
expect(serializer.send(:collapse_whitespace, "hello world")).to eq("hello world")
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
describe "#normalize_whitespace" do
|
|
133
|
+
it "replaces tabs with spaces" do
|
|
134
|
+
expect(serializer.send(:normalize_whitespace, "hello\tworld")).to eq("hello world")
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "replaces newlines with spaces" do
|
|
138
|
+
expect(serializer.send(:normalize_whitespace, "hello\nworld")).to eq("hello world")
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "replaces carriage returns with spaces" do
|
|
142
|
+
expect(serializer.send(:normalize_whitespace, "hello\rworld")).to eq("hello world")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "replaces mixed whitespace with a single space" do
|
|
146
|
+
expect(serializer.send(:normalize_whitespace, "hello \t\n\r world")).to eq("hello world")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "collapses multiple spaces into one" do
|
|
150
|
+
expect(serializer.send(:normalize_whitespace, "hello world")).to eq("hello world")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "handles a string with only whitespace" do
|
|
154
|
+
expect(serializer.send(:normalize_whitespace, "\t\n\r")).to eq(" ")
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "handles an empty string" do
|
|
158
|
+
expect(serializer.send(:normalize_whitespace, "")).to eq("")
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "does not modify a string with no extra whitespace" do
|
|
162
|
+
expect(serializer.send(:normalize_whitespace, "hello world")).to eq("hello world")
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
describe "#process_text_whitespace" do
|
|
167
|
+
context "when node preserves whitespace" do
|
|
168
|
+
it "returns text unchanged for code_block nodes" do
|
|
169
|
+
code_block = Prosereflect::CodeBlock.new
|
|
170
|
+
text = " hello\n world "
|
|
171
|
+
expect(serializer.send(:process_text_whitespace, text, code_block)).to eq(" hello\n world ")
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "returns text unchanged for code_block_wrapper nodes" do
|
|
175
|
+
wrapper = Prosereflect::CodeBlockWrapper.new
|
|
176
|
+
text = " hello\n world "
|
|
177
|
+
expect(serializer.send(:process_text_whitespace, text, wrapper)).to eq(" hello\n world ")
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "returns text unchanged for nodes with white-space: pre style" do
|
|
181
|
+
node = Prosereflect::Node.new("paragraph")
|
|
182
|
+
node.attrs = { "style" => "white-space: pre" }
|
|
183
|
+
text = " hello\n world "
|
|
184
|
+
expect(serializer.send(:process_text_whitespace, text, node)).to eq(" hello\n world ")
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
context "when node collapses whitespace" do
|
|
189
|
+
it "collapses whitespace for paragraph nodes" do
|
|
190
|
+
paragraph = Prosereflect::Paragraph.new
|
|
191
|
+
text = "hello world"
|
|
192
|
+
expect(serializer.send(:process_text_whitespace, text, paragraph)).to eq("hello world")
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it "collapses whitespace for heading nodes" do
|
|
196
|
+
heading = Prosereflect::Heading.new
|
|
197
|
+
heading.level = 1
|
|
198
|
+
text = "hello world"
|
|
199
|
+
expect(serializer.send(:process_text_whitespace, text, heading)).to eq("hello world")
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "collapses tabs and spaces for regular nodes" do
|
|
203
|
+
paragraph = Prosereflect::Paragraph.new
|
|
204
|
+
text = " hello \t world "
|
|
205
|
+
expect(serializer.send(:process_text_whitespace, text, paragraph)).to eq(" hello world ")
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
describe "DOMSerializer serialization" do
|
|
211
|
+
it "serializes a single node via #serialize_node" do
|
|
212
|
+
paragraph = Prosereflect::Paragraph.new
|
|
213
|
+
paragraph.add_text("Hello")
|
|
214
|
+
|
|
215
|
+
result = serializer.serialize_node(paragraph)
|
|
216
|
+
expect(result).to include("Hello")
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it "returns text unchanged via #render_text when no schema is set" do
|
|
220
|
+
text = "bold text"
|
|
221
|
+
bold_mark = Prosereflect::Mark::Bold.new
|
|
222
|
+
|
|
223
|
+
result = serializer.render_text(text, [bold_mark])
|
|
224
|
+
# Without a schema, build_mark_serializers returns {},
|
|
225
|
+
# so apply_mark returns content unmodified
|
|
226
|
+
expect(result).to eq("bold text")
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it "returns text unchanged via #render_text with multiple marks and no schema" do
|
|
230
|
+
text = "bold italic"
|
|
231
|
+
bold_mark = Prosereflect::Mark::Bold.new
|
|
232
|
+
italic_mark = Prosereflect::Mark::Italic.new
|
|
233
|
+
|
|
234
|
+
result = serializer.render_text(text, [bold_mark, italic_mark])
|
|
235
|
+
expect(result).to eq("bold italic")
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
it "renders text without marks via #render_text" do
|
|
239
|
+
result = serializer.render_text("plain text", [])
|
|
240
|
+
expect(result).to eq("plain text")
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it "renders text with nil marks via #render_text" do
|
|
244
|
+
result = serializer.render_text("plain text", nil)
|
|
245
|
+
expect(result).to eq("plain text")
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|