Class: MarkdownComposer::CompositionBuffer

Inherits:
Object
  • Object
show all
Defined in:
lib/markdown_composer/composition_buffer.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(markdown = +"",, diagnostics: Diagnostics.new) ⇒ CompositionBuffer

Returns a new instance of CompositionBuffer.



7
8
9
10
11
# File 'lib/markdown_composer/composition_buffer.rb', line 7

def initialize(markdown = +"", diagnostics: Diagnostics.new)
  @markdown = markdown.dup
  @diagnostics = diagnostics
  @origin_nodes = []
end

Instance Attribute Details

#diagnosticsObject (readonly)

Returns the value of attribute diagnostics.



5
6
7
# File 'lib/markdown_composer/composition_buffer.rb', line 5

def diagnostics
  @diagnostics
end

#origin_nodesObject (readonly)

Returns the value of attribute origin_nodes.



5
6
7
# File 'lib/markdown_composer/composition_buffer.rb', line 5

def origin_nodes
  @origin_nodes
end

Instance Method Details

#append(units) ⇒ Object



27
28
29
30
# File 'lib/markdown_composer/composition_buffer.rb', line 27

def append(units)
  @origin_nodes.concat(origin_units(units))
  insert_at_end(units_to_markdown(units))
end

#copy(target, units, options: {}) ⇒ Object



64
65
66
67
# File 'lib/markdown_composer/composition_buffer.rb', line 64

def copy(target, units, options: {})
  placement = target&.fetch("placement", nil) || "after"
  placement == "before" ? insert_before(target, units, options: options) : insert_after(target, units, options: options)
end

#dedupe_by_origin!Object



149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/markdown_composer/composition_buffer.rb', line 149

def dedupe_by_origin!
  seen = {}
  deduped = []
  origin_nodes.each do |node|
    key = node.attributes["origin_id"] || node.id
    next if seen[key]

    seen[key] = true
    deduped << node
  end
  set(deduped)
end

#empty?Boolean

Returns:

  • (Boolean)


17
18
19
# File 'lib/markdown_composer/composition_buffer.rb', line 17

def empty?
  @markdown.strip.empty?
end

#indexObject



125
126
127
# File 'lib/markdown_composer/composition_buffer.rb', line 125

def index
  DocumentIndex.from_markdown(@markdown, source_key: "buffer", diagnostics: diagnostics)
end

#insert_after(target, units, options: {}) ⇒ Object



41
42
43
# File 'lib/markdown_composer/composition_buffer.rb', line 41

def insert_after(target, units, options: {})
  insert_relative(target, units, :after, options: options)
end

#insert_before(target, units, options: {}) ⇒ Object



37
38
39
# File 'lib/markdown_composer/composition_buffer.rb', line 37

def insert_before(target, units, options: {})
  insert_relative(target, units, :before, options: options)
end

#insert_between(start_target, end_target, units, options: {}) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/markdown_composer/composition_buffer.rb', line 92

def insert_between(start_target, end_target, units, options: {})
  start_ranges = target_ranges(start_target, options: options)
  end_ranges = target_ranges(end_target, options: options)
  return diagnostics.warn("target.empty", "Between target matched no buffer content", path: "target") if start_ranges.empty? || end_ranges.empty?

  start_range = start_ranges.first
  end_range = end_ranges.first
  if start_range.end >= end_range.begin
    diagnostics.error("target.order_invalid", "Between start anchor must resolve before end anchor", path: "target")
    return self
  end

  @origin_nodes.concat(origin_units(units))
  buffer_lines = lines
  @markdown = join_markdown_fragments(
    buffer_lines[0...start_range.end].to_a.join,
    units_to_markdown(units),
    buffer_lines[start_range.end..].to_a.join
  )
  self
end

#markdownObject



13
14
15
# File 'lib/markdown_composer/composition_buffer.rb', line 13

def markdown
  @markdown.dup
end

#move(selector, target, units = nil, options: {}) ⇒ Object



114
115
116
117
118
119
120
121
122
123
# File 'lib/markdown_composer/composition_buffer.rb', line 114

def move(selector, target, units = nil, options: {})
  ranges = units ? unit_ranges(units) : target_ranges(selector, options: options)
  return warn_empty_destructive_target("move", path: "select") if ranges.empty?
  return warn_empty_destructive_target("move", path: "target") if target_ranges(target, options: options).empty?

  moved = units ? units_to_markdown(units) : ranges.map { |range| lines[(range.begin - 1)..(range.end - 1)].join }.join("\n")
  replace_ranges(ranges, "")
  unit = ComposerNode.new(id: "buffer:moved", source_key: "buffer", type: "paragraph", source_position: 0, level: nil, text: moved, attributes: {}, children: [], raw: moved, start_line: 1, end_line: moved.lines.length)
  copy(target, [ unit ], options: options)
end

#place_markdown(target, markdown, origin_nodes: [], options: {}) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/markdown_composer/composition_buffer.rb', line 69

def place_markdown(target, markdown, origin_nodes: [], options: {})
  target ||= { "position" => "end" }
  content = normalize_markdown_fragment(markdown)
  origins = Array(origin_nodes).compact

  case target["position"]
  when "output"
    @origin_nodes = origins
    @markdown = trim_outer_blank_lines(content)
    @markdown = normalize_markdown_fragment(@markdown) unless @markdown.empty?
  when "start"
    @origin_nodes = origins + @origin_nodes
    insert_at_start(content)
  when "end"
    @origin_nodes.concat(origins)
    insert_at_end(content)
  when nil
    place_markdown_relative(target, content, origins, target["placement"] == "before" ? :before : :after, options: options)
  end

  self
end

#prepend(units) ⇒ Object



32
33
34
35
# File 'lib/markdown_composer/composition_buffer.rb', line 32

def prepend(units)
  @origin_nodes = origin_units(units) + @origin_nodes
  insert_at_start(units_to_markdown(units))
end

#remove(target, options: {}) ⇒ Object



57
58
59
60
61
62
# File 'lib/markdown_composer/composition_buffer.rb', line 57

def remove(target, options: {})
  ranges = target_ranges(target, options: options)
  return warn_empty_destructive_target("remove_buffer_target", path: "target") if ranges.empty?

  replace_ranges(ranges, "")
end

#replace(target, units, options: {}) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/markdown_composer/composition_buffer.rb', line 45

def replace(target, units, options: {})
  if target && target["position"] == "output"
    return set(units)
  end

  ranges = target_ranges(target, options: options)
  return warn_empty_destructive_target("replace", path: "target") if ranges.empty?

  @origin_nodes.concat(origin_units(units))
  replace_ranges(ranges, units_to_markdown(units))
end

#replace_markdown(markdown) ⇒ Object



129
130
131
132
# File 'lib/markdown_composer/composition_buffer.rb', line 129

def replace_markdown(markdown)
  @markdown = markdown.to_s
  self
end

#replace_markdown_ranges(replacements) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/markdown_composer/composition_buffer.rb', line 134

def replace_markdown_ranges(replacements)
  buffer_lines = lines
  Array(replacements).sort_by { |replacement| replacement.fetch(:range).begin }.reverse_each do |replacement|
    range = replacement.fetch(:range)
    content = replacement.fetch(:markdown).to_s
    start = [ range.begin - 1, 0 ].max
    finish = [ range.end - 1, buffer_lines.length - 1 ].min
    replacement_lines = content.empty? ? [] : [ normalize_markdown_fragment(content) ]
    buffer_lines[start..finish] = replacement_lines
  end
  @markdown = buffer_lines.join
  sync_origins_to_current!
  self
end

#set(units) ⇒ Object



21
22
23
24
25
# File 'lib/markdown_composer/composition_buffer.rb', line 21

def set(units)
  @markdown = units_to_markdown(units)
  @origin_nodes = origin_units(units)
  self
end

#sync_origins_to_current!Object



162
163
164
165
# File 'lib/markdown_composer/composition_buffer.rb', line 162

def sync_origins_to_current!
  @origin_nodes = index.nodes
  self
end

#to_hObject



167
168
169
170
171
172
173
174
175
# File 'lib/markdown_composer/composition_buffer.rb', line 167

def to_h
  {
    type: "composition_buffer",
    markdown: markdown,
    origins: origin_nodes.map { |node| { id: node.id, origin_id: node.attributes["origin_id"], source_key: node.source_key, source_position: node.source_position } },
    nodes: index.nodes.map(&:to_h),
    sections: index.sections.map(&:to_h)
  }
end