require 'erubi' require 'html_tokenizer' require_relative 'token' require_relative 'location' module BetterHtml class NodeIterator class HtmlErb < ::Erubi::Engine attr_reader :tokens attr_reader :parser REGEXP_WITHOUT_TRIM = /<%(={1,2}|-|%)?(.*?)(?:[-=])?()?%>([ \t]*\r?\n)?/m def initialize(document) @parser = HtmlTokenizer::Parser.new @tokens = [] super(document, regexp: REGEXP_WITHOUT_TRIM, trim: false) end def add_text(text) @parser.parse(text) { |*args| add_tokens(*args) } end def add_code(code) text = "<%#{code}%>" start = @parser.document_length stop = start + text.size @tokens << Token.new( type: :stmt, code: code, text: text, location: Location.new(start, stop, @parser.line_number, @parser.column_number) ) @parser.append_placeholder(text) end def add_expression(indicator, code) text = "<%#{indicator}#{code}%>" start = @parser.document_length stop = start + text.size @tokens << Token.new( type: indicator == '=' ? :expr_literal : :expr_escaped, code: code, text: text, location: Location.new(start, stop, @parser.line_number, @parser.column_number) ) @parser.append_placeholder(text) end private def add_tokens(type, start, stop, line, column) extra_attributes = if type == :tag_end { self_closing: @parser.self_closing_tag? } end @tokens << Token.new( type: type, text: @parser.extract(start, stop), location: Location.new(start, stop, line, column), **(extra_attributes || {}) ) end end end end