module Oppen
class PrintStack
class PrintStackEntry
attr_reader :offset
attr_reader :break_type
def initialize(offset, break_type)
@offset = offset
@break_type = break_type
end
end
attr_reader :buffer
attr_reader :config
attr_reader :genspace
attr_reader :items
attr_reader :new_line
attr_reader :width
attr_reader :space
def initialize(width, new_line, config, space, out)
@buffer = out
@config = config
@genspace =
if space.respond_to?(:call)
raise ArgumentError, 'space argument must be a Proc of arity 1' \
if space.to_proc.arity != 1
space
else
->(n) { space * n }
end
@items = []
@new_line = new_line
@width = width
@space = width
end
def output
buffer.string
end
def print(token, token_width)
case token
in Token::Begin
handle_begin token, token_width
in Token::End
handle_end
in Token::Break
handle_break token, token_width
in Token::String
handle_string token, token_width
end
end
def handle_begin(token, token_width)
if token_width > space
type =
if token.break_type == Token::BreakType::CONSISTENT
Token::BreakType::CONSISTENT
else
Token::BreakType::INCONSISTENT
end
if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
indent = token.offset
if !items.empty?
indent += top.offset
end
else
indent = space - token.offset
end
push PrintStackEntry.new indent, type
else
push PrintStackEntry.new 0, Token::BreakType::FITS
end
end
def handle_end
pop
end
def handle_break(token, token_width)
block = top
case block.break_type
in Token::BreakType::FITS
@space -= token.width
write token
in Token::BreakType::CONSISTENT
@space = block.offset - token.offset
indent =
if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
token.offset
else
width - space
end
write token.line_continuation
print_new_line indent
in Token::BreakType::INCONSISTENT
if token_width > space
@space = block.offset - token.offset
indent =
if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
token.offset
else
width - space
end
write token.line_continuation
print_new_line indent
else
@space -= token.width
write token
end
end
end
def handle_string(token, token_width)
@space = [0, space - token_width].max
write token
end
def push(print_stack_entry)
items.append(print_stack_entry)
end
def pop
if items.empty?
raise 'Popping empty stack'
end
items.pop
end
def top
if items.empty?
raise 'Accessing empty stack'
end
items.last
end
def print_new_line(amount)
write new_line
if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
@space = width - top.offset - amount
indent width - space
else
indent amount
end
end
def write(obj)
buffer.write(obj.to_s)
end
def indent(amount)
write genspace.call(amount)
end
end
end