Skip to content

Instantly share code, notes, and snippets.

@clarkenciel
Created February 24, 2019 00:02
Show Gist options
  • Save clarkenciel/68b77d7884cd6e4f240e9c65a6f2b455 to your computer and use it in GitHub Desktop.
Save clarkenciel/68b77d7884cd6e4f240e9c65a6f2b455 to your computer and use it in GitHub Desktop.
building up to block elements from quill delta ops
class Line
include Enumerable
delegate :each, to: :@elements
attr_reader :elements, :attributes
def initialize elements: [], attributes: {}
@elements = elements
@attributes = Attributes.new(**(attributes || {}))
end
def closed?
@elements.last.closed?
end
def join other
Line.new elements: @elements + other.elements,
attributes: @attributes.merge(other.attributes)
end
class Attributes < Hash
VALID_ATTRS = %i(
list
indent
header
)
def initialize **args
args.select { |k, v| VALID_ATTRS.include? k }.each do |k, v|
self[k] = v
end
end
end
end
class Text
attr_reader :content, :attributes
def initialize content: '', attributes: {}
@content = content
@attributes = Attributes.new(**(attributes || {}))
end
def closed?
@content.ends_with? "\n"
end
class Attributes < Hash
INVALID_ATTRS = %i(
list
indent
header
)
def initialize **args
args.reject { |k, v| INVALID_ATTRS.include? k }.each do |k, v|
self[k] = v
end
end
end
end
class Lines
include Enumerable
delegate :each, to: :lines
attr_reader :lines
def initialize lines: []
@lines = lines
end
def self.from_ops ops
ops.inject(new) { |lines, op| lines.apply op }
end
def apply op
lines = op[:insert].split(/(?<=\n)/).map do |t|
Line.new elements: [Text.new(content: t, attributes: op[:attributes])],
attributes: op[:attributes]
end
return Lines.new lines: lines if @lines.empty?
return Lines.new lines: @lines + lines if @lines.last.closed?
Lines.new lines: [
*@lines[0...-1],
@lines.last.join(lines.first),
*lines[1..-1],
]
end
def blocks
Blocks.from_lines @lines
end
end
class Block
def self.containing lines
new lines: lines, attributes: lines.first.attributes
end
attr_reader :attributes
def initialize lines: [], attributes: {}
@lines = lines
@attributes = attributes
end
end
class Blocks
include Enumerable
def self.from_lines lines
last_line = nil
new(blocks: lines.slice_before do |line|
is_block_break = last_line&.attributes != line.attributes
last_line = line
is_block_break
end.map { |lines| block.containing lines })
end
delegate :each, to: :blocks
attr_reader :blocks
def initialize blocks: []
@blocks = blocks
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment