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
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
# {
|
|
6
4
|
# type: "strike"
|
|
7
5
|
# }
|
|
@@ -9,7 +7,7 @@ require_relative 'base'
|
|
|
9
7
|
module Prosereflect
|
|
10
8
|
module Mark
|
|
11
9
|
class Strike < Base
|
|
12
|
-
PM_TYPE =
|
|
10
|
+
PM_TYPE = "strike"
|
|
13
11
|
end
|
|
14
12
|
end
|
|
15
13
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
# {
|
|
6
4
|
# type: "subscript"
|
|
7
5
|
# }
|
|
@@ -9,7 +7,7 @@ require_relative 'base'
|
|
|
9
7
|
module Prosereflect
|
|
10
8
|
module Mark
|
|
11
9
|
class Subscript < Base
|
|
12
|
-
PM_TYPE =
|
|
10
|
+
PM_TYPE = "subscript"
|
|
13
11
|
end
|
|
14
12
|
end
|
|
15
13
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
# {
|
|
6
4
|
# type: "superscript"
|
|
7
5
|
# }
|
|
@@ -9,7 +7,7 @@ require_relative 'base'
|
|
|
9
7
|
module Prosereflect
|
|
10
8
|
module Mark
|
|
11
9
|
class Superscript < Base
|
|
12
|
-
PM_TYPE =
|
|
10
|
+
PM_TYPE = "superscript"
|
|
13
11
|
end
|
|
14
12
|
end
|
|
15
13
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
# {
|
|
6
4
|
# type: "underline"
|
|
7
5
|
# }
|
|
@@ -9,7 +7,7 @@ require_relative 'base'
|
|
|
9
7
|
module Prosereflect
|
|
10
8
|
module Mark
|
|
11
9
|
class Underline < Base
|
|
12
|
-
PM_TYPE =
|
|
10
|
+
PM_TYPE = "underline"
|
|
13
11
|
end
|
|
14
12
|
end
|
|
15
13
|
end
|
data/lib/prosereflect/mark.rb
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module Prosereflect
|
|
4
4
|
module Mark
|
|
5
|
+
autoload :Base, "#{__dir__}/mark/base"
|
|
6
|
+
autoload :Bold, "#{__dir__}/mark/bold"
|
|
7
|
+
autoload :Italic, "#{__dir__}/mark/italic"
|
|
8
|
+
autoload :Code, "#{__dir__}/mark/code"
|
|
9
|
+
autoload :Link, "#{__dir__}/mark/link"
|
|
10
|
+
autoload :Strike, "#{__dir__}/mark/strike"
|
|
11
|
+
autoload :Subscript, "#{__dir__}/mark/subscript"
|
|
12
|
+
autoload :Superscript, "#{__dir__}/mark/superscript"
|
|
13
|
+
autoload :Underline, "#{__dir__}/mark/underline"
|
|
5
14
|
end
|
|
6
15
|
end
|
|
7
|
-
|
|
8
|
-
require_relative 'mark/bold'
|
|
9
|
-
require_relative 'mark/italic'
|
|
10
|
-
require_relative 'mark/code'
|
|
11
|
-
require_relative 'mark/link'
|
data/lib/prosereflect/node.rb
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'lutaml/model'
|
|
4
|
-
require_relative 'attribute'
|
|
5
|
-
require_relative 'mark'
|
|
6
|
-
|
|
7
3
|
module Prosereflect
|
|
8
4
|
class Node < Lutaml::Model::Serializable
|
|
9
|
-
PM_TYPE =
|
|
5
|
+
PM_TYPE = "node"
|
|
10
6
|
|
|
11
7
|
attribute :type, :string
|
|
12
8
|
attribute :attrs, :hash
|
|
@@ -14,10 +10,10 @@ module Prosereflect
|
|
|
14
10
|
attribute :content, Node, polymorphic: true, collection: true
|
|
15
11
|
|
|
16
12
|
key_value do
|
|
17
|
-
map
|
|
18
|
-
map
|
|
19
|
-
map
|
|
20
|
-
map
|
|
13
|
+
map "type", to: :type, render_default: true
|
|
14
|
+
map "attrs", to: :attrs
|
|
15
|
+
map "marks", to: :marks
|
|
16
|
+
map "content", to: :content
|
|
21
17
|
end
|
|
22
18
|
|
|
23
19
|
def initialize(data = nil, attrs = nil)
|
|
@@ -25,18 +21,18 @@ module Prosereflect
|
|
|
25
21
|
super(type: data, attrs: attrs, content: [])
|
|
26
22
|
elsif data.is_a?(Hash)
|
|
27
23
|
# Handle marks in a special way to preserve expected behavior in tests
|
|
28
|
-
if data[:marks] || data[
|
|
29
|
-
marks_data = data[:marks] || data[
|
|
24
|
+
if data[:marks] || data["marks"]
|
|
25
|
+
marks_data = data[:marks] || data["marks"]
|
|
30
26
|
data = data.dup
|
|
31
|
-
data.delete(
|
|
27
|
+
data.delete("marks")
|
|
32
28
|
data.delete(:marks)
|
|
33
29
|
super(data)
|
|
34
30
|
self.marks = marks_data
|
|
35
31
|
else
|
|
36
32
|
# Handle attrs properly
|
|
37
|
-
if data[:attrs] || data[
|
|
33
|
+
if data[:attrs] || data["attrs"]
|
|
38
34
|
data = data.dup
|
|
39
|
-
data[:attrs] = process_attrs_data(data[:attrs] || data[
|
|
35
|
+
data[:attrs] = process_attrs_data(data[:attrs] || data["attrs"])
|
|
40
36
|
end
|
|
41
37
|
super(data)
|
|
42
38
|
end
|
|
@@ -56,33 +52,35 @@ module Prosereflect
|
|
|
56
52
|
def self.create(type = nil, attrs = nil)
|
|
57
53
|
new(type || self::PM_TYPE, attrs)
|
|
58
54
|
rescue NameError
|
|
59
|
-
new(type ||
|
|
55
|
+
new(type || "node", attrs)
|
|
60
56
|
end
|
|
61
57
|
|
|
62
58
|
# Convert to hash for serialization
|
|
63
59
|
def to_h
|
|
64
|
-
result = {
|
|
60
|
+
result = { "type" => type }
|
|
65
61
|
|
|
66
62
|
if attrs && !attrs.empty?
|
|
67
63
|
if attrs.is_a?(Hash)
|
|
68
|
-
result[
|
|
69
|
-
elsif attrs.is_a?(Array) && attrs.all?
|
|
64
|
+
result["attrs"] = process_node_attributes(attrs, type)
|
|
65
|
+
elsif attrs.is_a?(Array) && attrs.all? do |attr|
|
|
66
|
+
attr.respond_to?(:to_h)
|
|
67
|
+
end
|
|
70
68
|
# Convert array of attribute objects to a hash
|
|
71
69
|
attrs_array = attrs.map do |attr|
|
|
72
70
|
attr.is_a?(Prosereflect::Attribute::Base) ? attr.to_h : attr
|
|
73
71
|
end
|
|
74
|
-
result[
|
|
72
|
+
result["attrs"] = attrs_array unless attrs_array.empty?
|
|
75
73
|
end
|
|
76
74
|
end
|
|
77
75
|
|
|
78
76
|
if marks && !marks.empty?
|
|
79
|
-
result[
|
|
77
|
+
result["marks"] = marks.map do |mark|
|
|
80
78
|
if mark.is_a?(Hash)
|
|
81
79
|
mark
|
|
82
80
|
elsif mark.respond_to?(:to_h)
|
|
83
81
|
mark.to_h
|
|
84
82
|
elsif mark.respond_to?(:type)
|
|
85
|
-
{
|
|
83
|
+
{ "type" => mark.type.to_s }
|
|
86
84
|
else
|
|
87
85
|
raise ArgumentError, "Invalid mark type: #{mark.class}"
|
|
88
86
|
end
|
|
@@ -90,8 +88,10 @@ module Prosereflect
|
|
|
90
88
|
end
|
|
91
89
|
|
|
92
90
|
if content && !content.empty?
|
|
93
|
-
result[
|
|
94
|
-
content.map
|
|
91
|
+
result["content"] = if content.is_a?(Array)
|
|
92
|
+
content.map do |item|
|
|
93
|
+
item.respond_to?(:to_h) ? item.to_h : item
|
|
94
|
+
end
|
|
95
95
|
else
|
|
96
96
|
[content]
|
|
97
97
|
end
|
|
@@ -112,7 +112,7 @@ module Prosereflect
|
|
|
112
112
|
elsif mark.respond_to?(:to_h)
|
|
113
113
|
mark.to_h
|
|
114
114
|
elsif mark.respond_to?(:type)
|
|
115
|
-
{
|
|
115
|
+
{ "type" => mark.type.to_s }
|
|
116
116
|
else
|
|
117
117
|
raise ArgumentError, "Invalid mark type: #{mark.class}"
|
|
118
118
|
end
|
|
@@ -131,8 +131,8 @@ module Prosereflect
|
|
|
131
131
|
elsif value.is_a?(Array)
|
|
132
132
|
@marks = value.map do |v|
|
|
133
133
|
if v.is_a?(Hash)
|
|
134
|
-
type = v[
|
|
135
|
-
attrs = v[
|
|
134
|
+
type = v["type"] || v[:type]
|
|
135
|
+
attrs = v["attrs"] || v[:attrs]
|
|
136
136
|
begin
|
|
137
137
|
mark_class = Prosereflect::Mark.const_get(type.to_s.capitalize)
|
|
138
138
|
mark_class.new(attrs: attrs)
|
|
@@ -197,27 +197,165 @@ module Prosereflect
|
|
|
197
197
|
def find_children(node_type)
|
|
198
198
|
return [] unless content
|
|
199
199
|
|
|
200
|
-
content.
|
|
200
|
+
content.grep(node_type)
|
|
201
201
|
end
|
|
202
202
|
|
|
203
203
|
def text_content
|
|
204
|
-
return
|
|
204
|
+
return "" unless content
|
|
205
205
|
|
|
206
206
|
content.map(&:text_content).join
|
|
207
207
|
end
|
|
208
208
|
|
|
209
|
+
# Size of this node in the document tree.
|
|
210
|
+
# For non-text nodes: 1 (opening token) + sum of children's node_size.
|
|
211
|
+
# For text nodes: overridden to text.length + 1.
|
|
212
|
+
def node_size
|
|
213
|
+
size = 1
|
|
214
|
+
content&.each { |child| size += child.node_size }
|
|
215
|
+
size
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Whether this node represents a text node.
|
|
219
|
+
# Overridden to true in Text class.
|
|
220
|
+
def text?
|
|
221
|
+
false
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Return a copy of this node with content restricted to the given range.
|
|
225
|
+
# Positions are relative to the start of this node's content.
|
|
226
|
+
def cut(from = 0, to = nil)
|
|
227
|
+
to ||= node_size
|
|
228
|
+
return self if from.zero? && to == node_size
|
|
229
|
+
|
|
230
|
+
if text?
|
|
231
|
+
# Text nodes override this
|
|
232
|
+
self
|
|
233
|
+
else
|
|
234
|
+
copy(cut_content(from, to))
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Iterate over all nodes between two positions in this node.
|
|
239
|
+
# Accepts a block or a callable as the third positional argument.
|
|
240
|
+
def nodes_between(from, to, callback = nil, node_start = 0, &block)
|
|
241
|
+
cb = callback || block
|
|
242
|
+
return unless cb && to > from && content
|
|
243
|
+
|
|
244
|
+
pos = 0
|
|
245
|
+
content.each_with_index do |child, i|
|
|
246
|
+
break if pos >= to
|
|
247
|
+
|
|
248
|
+
child_end = pos + child.node_size
|
|
249
|
+
next unless child_end > from
|
|
250
|
+
|
|
251
|
+
child_start = node_start + pos + 1
|
|
252
|
+
if cb.call(child, child_start, i) != false && child.content && child.content.any?
|
|
253
|
+
child.nodes_between(
|
|
254
|
+
[0, from - pos - 1].max,
|
|
255
|
+
[child.content ? child.content.size : 0, to - pos - 1].min,
|
|
256
|
+
cb,
|
|
257
|
+
child_start,
|
|
258
|
+
)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
pos = child_end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Iterate over all descendant nodes.
|
|
266
|
+
def descendants(&block)
|
|
267
|
+
nodes_between(0, node_size - 1, &block)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Check structural equality with another node.
|
|
271
|
+
def eq?(other)
|
|
272
|
+
return false unless other.is_a?(Node)
|
|
273
|
+
|
|
274
|
+
type == other.type && to_h == other.to_h
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Create a copy of this node with different content.
|
|
278
|
+
def copy(new_content = nil)
|
|
279
|
+
new_node = self.class.new(type: type, attrs: attrs, marks: raw_marks)
|
|
280
|
+
case new_content
|
|
281
|
+
when nil
|
|
282
|
+
# no content
|
|
283
|
+
when Array
|
|
284
|
+
new_node.content = new_content
|
|
285
|
+
when Fragment
|
|
286
|
+
new_node.content = new_content.to_a
|
|
287
|
+
else
|
|
288
|
+
new_node.content = [new_content]
|
|
289
|
+
end
|
|
290
|
+
new_node
|
|
291
|
+
end
|
|
292
|
+
|
|
209
293
|
# Ensures YAML serialization outputs plain data instead of a Ruby object
|
|
210
294
|
def to_yaml(*args)
|
|
211
295
|
to_h.to_yaml(*args)
|
|
212
296
|
end
|
|
213
297
|
|
|
298
|
+
# Resolve a document position to a ResolvedPos
|
|
299
|
+
def resolve(pos)
|
|
300
|
+
path = []
|
|
301
|
+
build_path_for_pos(pos, path)
|
|
302
|
+
depth = [(path.length / 3) - 1, 0].max
|
|
303
|
+
ResolvedPos.new(pos, path, depth)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Get the node at a given depth in the path
|
|
307
|
+
def node(depth)
|
|
308
|
+
@path[depth * 2]
|
|
309
|
+
end
|
|
310
|
+
|
|
214
311
|
private
|
|
215
312
|
|
|
216
|
-
def
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
313
|
+
def cut_content(from, to)
|
|
314
|
+
return [] unless content
|
|
315
|
+
|
|
316
|
+
result = []
|
|
317
|
+
pos = 0
|
|
318
|
+
content.each do |child|
|
|
319
|
+
child_end = pos + child.node_size
|
|
320
|
+
if pos >= from && child_end <= to
|
|
321
|
+
result << child
|
|
322
|
+
elsif pos < to && child_end > from
|
|
323
|
+
result << child.cut([0, from - pos - 1].max, child.node_size - [0, child_end - to].max)
|
|
324
|
+
end
|
|
325
|
+
pos = child_end
|
|
326
|
+
break if pos >= to
|
|
327
|
+
end
|
|
328
|
+
result
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def build_path_for_pos(pos, path, index = 0, start_offset = 0)
|
|
332
|
+
path << self << index << start_offset
|
|
333
|
+
return if pos.zero?
|
|
334
|
+
|
|
335
|
+
traverse_children_for_resolve(pos, path)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def traverse_children_for_resolve(pos, path)
|
|
339
|
+
return unless content
|
|
340
|
+
|
|
341
|
+
content_offset = 1
|
|
342
|
+
child_index = 0
|
|
343
|
+
|
|
344
|
+
content.each do |child|
|
|
345
|
+
child_end = content_offset + child.node_size
|
|
346
|
+
if pos < child_end
|
|
347
|
+
child.send(:build_path_for_pos, pos - content_offset, path, child_index, content_offset)
|
|
348
|
+
return
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
content_offset = child_end
|
|
352
|
+
child_index += 1
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def process_node_attributes(attrs, _node_type)
|
|
357
|
+
if attrs["attrs"].is_a?(Hash)
|
|
358
|
+
attrs["attrs"]
|
|
221
359
|
else
|
|
222
360
|
attrs
|
|
223
361
|
end
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'node'
|
|
4
|
-
require_relative 'list_item'
|
|
5
|
-
|
|
6
3
|
module Prosereflect
|
|
7
4
|
# OrderedList class represents a numbered list in ProseMirror.
|
|
8
5
|
class OrderedList < Node
|
|
9
|
-
PM_TYPE =
|
|
6
|
+
PM_TYPE = "ordered_list"
|
|
10
7
|
|
|
11
|
-
attribute :type, :string, default: -> {
|
|
8
|
+
attribute :type, :string, default: -> {
|
|
9
|
+
self.class.send(:const_get, "PM_TYPE")
|
|
10
|
+
}
|
|
12
11
|
attribute :start, :integer
|
|
13
12
|
attribute :attrs, :hash
|
|
14
13
|
|
|
15
14
|
key_value do
|
|
16
|
-
map
|
|
17
|
-
map
|
|
18
|
-
map
|
|
15
|
+
map "type", to: :type, render_default: true
|
|
16
|
+
map "attrs", to: :attrs
|
|
17
|
+
map "content", to: :content
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def initialize(attributes = {})
|
|
@@ -29,12 +28,14 @@ module Prosereflect
|
|
|
29
28
|
|
|
30
29
|
def start=(value)
|
|
31
30
|
@start = value
|
|
31
|
+
return if value.nil?
|
|
32
|
+
|
|
32
33
|
self.attrs ||= {}
|
|
33
|
-
attrs[
|
|
34
|
+
attrs["start"] = value
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def start
|
|
37
|
-
@start || attrs&.[](
|
|
38
|
+
@start || attrs&.[]("start") || 1
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def add_item(text)
|
|
@@ -67,19 +68,21 @@ module Prosereflect
|
|
|
67
68
|
# Update the order (1 for numerical, 'a' for alphabetical, etc.)
|
|
68
69
|
def order=(order_value)
|
|
69
70
|
self.attrs ||= {}
|
|
70
|
-
attrs[
|
|
71
|
+
attrs["order"] = order_value
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
# Get the order value
|
|
74
75
|
def order
|
|
75
|
-
attrs&.[](
|
|
76
|
+
attrs&.[]("order") || 1
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
# Get text content with proper formatting
|
|
79
80
|
def text_content
|
|
80
|
-
return
|
|
81
|
+
return "" unless content
|
|
81
82
|
|
|
82
|
-
content.map
|
|
83
|
+
content.map do |item|
|
|
84
|
+
item.respond_to?(:text_content) ? item.text_content : ""
|
|
85
|
+
end.join("\n")
|
|
83
86
|
end
|
|
84
87
|
end
|
|
85
88
|
end
|