[
  {
    "path": ".gitignore",
    "content": "*.gem\nGemfile.lock\n/tmp\n.ruby-version\n"
  },
  {
    "path": ".rspec",
    "content": "--color\n--require spec_helper\n-I lib\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "inherit_mode:\n  merge:\n    - Exclude\n\nrequire:\n  - standard\n  - rubocop-rake\n\ninherit_gem:\n  standard: config/base.yml\n\nAllCops:\n  NewCops: disable\n  SuggestExtensions: false\n  TargetRubyVersion: 3.2\n  Exclude:\n    - '**/*.gemspec'\n    - '**/Rakefile'\n"
  },
  {
    "path": "COPYING.txt",
    "content": "Copyright (c) 2007-2015 Paul Battley\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Gemfile",
    "content": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\ngemspec\n"
  },
  {
    "path": "History.txt",
    "content": "== 1.3.1 (2017-05-04)\n* Fix erroneous additional indentation being applied to code in <script> and\n  <style> sections.\n\n== 1.3.0 (2017-03-20)\n* Allow blank lines (up to a maximum) to be preserved in output.\n* Fix bug with excess indentation in some circumstances.\n\n== 1.2.1 (2016-11-22)\n* Support arbitrary self-closing tags.\n\n== 1.2.0 (2016-09-06)\n* Support indentation via tabs.\n* Allow the whole output to be indented by a number of steps.\n* Indentation is now handled by the indent option: tab_stops still works but\n  is deprecated.\n\n== 1.1.1 (2015-07-27)\n* Indent after 'until' and 'for'.\n* Do not modify the content of <textarea>.\n* Improve documentation.\n* Make coding style consistent (and enforced by Robocop).\n\n== 1.1.0 (2015-03-07)\n* Remove whitespace in an otherwise-empty <script></script> node.\n\n== 1.0.2 (2015-02-23)\n* Allow '<' in attributes in order to support AngularJS.\n\n== 1.0.1 (2015-02-22)\n* Improve help output of command-line tool.\n\n== 1.0.0 (2015-01-19)\n* Improve and document the API.\n* Specify Ruby support: >= 1.9.2.\n* Move tests to RSpec.\n* Stop breaking on excessive outdenting by default.\n\n== 0.0.12 (2014-12-30)\n* Add new lines after <br> and around <pre>.\n* Add HTML5 block elements and remove those deprecated in HTML 4.0.\n* Fix breakage in command-line tool.\n* Command-line tool is now tested.\n* No longer hangs on certain large files.\n\n== 0.0.11 (2014-12-29)\n* Preserve formatting inside <pre>.\n* Add new lines after block-like elements.\n\n== 0.0.10 (2014-09-28)\n* Set tab width via CLI option.\n\n== 0.0.9 (2013-12-29)\n* Support <br> etc. without /.\n* Make element names case-insensitive.\n\n== 0.0.8 (2013-08-27)\n* Avoid wiping output file on error when working in place.\n* Report filename when an error occurs.\n* Clarify licence (with contributor permission): MIT.\n\n== 0.0.7 (2012-07-10)\n* Modernise gem structure.\n* Document Beautifier.\n* Improve outdent reporting.\n\n== 0.0.6 (2010-07-01)\n* Fix new line at end of output when modifying file.\n\n== 0.0.5 (2010-07-01)\n* Add option to modify file in place.\n* Report source line when outdenting too far.\n\n== 0.0.4 (2009-10-13)\n* Outdent 'else' correctly.\n\n== 0.0.3 (2009-10-13)\n* Support <%- ... -%>\n* Eliminated dependency on hoe.\n\n== 0.0.2 (2009-10-11)\n* Move from a single file to multiple files.\n* Fix parsing of standalone element immediately after closing element.\n* Don't break on empty script elements.\n* Emit new line at end of output.\n* Parse IE conditional comments.\n* Release as a gem.\n"
  },
  {
    "path": "README.md",
    "content": "# HTML Beautifier\n\nA normaliser/beautifier for HTML that also understands embedded Ruby.\nIdeal for tidying up Rails templates.\n\n## What it does\n\n* Normalises hard tabs to spaces (or vice versa)\n* Removes trailing spaces\n* Indents after opening HTML elements\n* Outdents before closing elements\n* Collapses multiple whitespace\n* Indents after block-opening embedded Ruby (if, do etc.)\n* Outdents before closing Ruby blocks\n* Outdents elsif and then indents again\n* Indents the left-hand margin of JavaScript and CSS blocks to match the\n  indentation level of the code\n\n## Usage\n\n### From the command line\n\nTo update files in-place:\n\n``` sh\n$ htmlbeautifier file1.html.erb [file2.html.erb ...]\n```\n\nor to operate on standard input and output:\n\n``` sh\n$ htmlbeautifier < untidy.html.erb > formatted.html.erb\n```\n\n### In your code\n\n```ruby\nrequire 'htmlbeautifier'\n\nbeautiful = HtmlBeautifier.beautify(untify_html_string)\n```\n\nYou can also specify how to indent (the default is two spaces):\n\n```ruby\nbeautiful = HtmlBeautifier.beautify(untidy_html_string, indent: \"\\t\")\n```\n\n## Installation\n\nThis is a Ruby gem.\nTo install the command-line tool (you may need `sudo`):\n\n```sh\n$ gem install htmlbeautifier\n```\n\nTo use the gem with Bundler, add to your `Gemfile`:\n\n```ruby\ngem 'htmlbeautifier'\n```\n\n## Contributing\n\n1. Follow [these guidelines][git-commit] when writing commit messages (briefly,\n   the first line should begin with a capital letter, use the imperative mood,\n   be no more than 50 characters, and not end with a period).\n2. Include tests.\n\n[git-commit]:http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\n"
  },
  {
    "path": "Rakefile",
    "content": "require \"rspec/core/rake_task\"\n\ndesc \"Run the specs.\"\nRSpec::Core::RakeTask.new do |t|\n  t.pattern = \"spec/**/*_spec.rb\"\n  t.verbose = false\nend\n\ntask :default => [:spec]\n\nif Gem.loaded_specs.key?('rubocop')\n  require 'rubocop/rake_task'\n  RuboCop::RakeTask.new\n\n  task(:default).prerequisites << task(:rubocop)\nend\n"
  },
  {
    "path": "bin/htmlbeautifier",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire \"htmlbeautifier\"\nrequire \"optparse\"\nrequire \"fileutils\"\nrequire \"stringio\"\n\ndef beautify(name, input, output, options)\n  output.puts HtmlBeautifier.beautify(input, options)\nrescue => e\n  raise \"Error parsing #{name}: #{e}\"\nend\n\nexecutable = File.basename(__FILE__)\n\noptions = {indent: \"  \"}\nparser = OptionParser.new do |opts|\n  opts.banner = \"Usage: #{executable} [options] [file ...]\"\n  opts.separator <<~STRING\n\n    #{executable} has two modes of operation:\n\n    1. If no files are listed, it will read from standard input and write to\n       standard output.\n    2. If files are listed, it will modify each file in place, overwriting it\n       with the beautified output.\n\n    The following options are available:\n\n  STRING\n  opts.on(\n    \"-t\", \"--tab-stops NUMBER\", Integer,\n    \"Set number of spaces per indent (default #{options[:tab_stops]})\"\n  ) do |num|\n    options[:indent] = \" \" * num\n  end\n  opts.on(\n    \"-T\", \"--tab\",\n    \"Indent using tabs\"\n  ) do\n    options[:indent] = \"\\t\"\n  end\n  opts.on(\n    \"-i\", \"--indent-by NUMBER\", Integer,\n    \"Indent the output by NUMBER steps (default 0).\"\n  ) do |num|\n    options[:initial_level] = num\n  end\n  opts.on(\n    \"-e\", \"--stop-on-errors\",\n    \"Stop when invalid nesting is encountered in the input\"\n  ) do |num|\n    options[:stop_on_errors] = num\n  end\n  opts.on(\n    \"-b\", \"--keep-blank-lines NUMBER\", Integer,\n    \"Set number of consecutive blank lines\"\n  ) do |num|\n    options[:keep_blank_lines] = num\n  end\n  opts.on(\n    \"-l\", \"--lint-only\",\n    \"Lint only, error on files which would be modified\",\n    \"This is not available when reading from standard input\"\n  ) do |num|\n    options[:lint_only] = num\n  end\n  opts.on(\n    \"-v\", \"--version\",\n    \"Display version and exit\"\n  ) do\n    puts HtmlBeautifier::VERSION::STRING\n    exit\n  end\n  opts.on(\n    \"-h\", \"--help\",\n    \"Display this help message and exit\"\n  ) do\n    puts opts\n    exit\n  end\nend\n\nparser.parse!\n\nif ARGV.any?\n  failures = []\n  ARGV.each do |path|\n    input = File.read(path)\n    if options[:lint_only]\n      output = StringIO.new\n      beautify path, input, output, options\n      failures << path unless input == output.string\n    else\n      temppath = \"#{path}.tmp\"\n      File.open(temppath, \"w\") do |file|\n        beautify path, input, file, options\n      end\n      FileUtils.mv temppath, path\n    end\n  end\n  unless failures.empty?\n    warn [\n      \"Lint failed - files would be modified:\",\n      *failures\n    ].join(\"\\n\")\n    exit 1\n  end\nelse\n  beautify \"standard input\", $stdin.read, $stdout, options\nend\n"
  },
  {
    "path": "contributors.txt",
    "content": "Paul Battley <pbattley@gmail.com>\nChris Berkhout <chrisberkhout@gmail.com>\nMatti Lehtonen <matti.lehtonen@puujaa.com>\nJakub Jirutka <jakub@jirutka.cz>\nJoe Rossi <jrossi@arn.com>\nMike Kozono <mkozono@gmail.com>\nNicholas Rutherford <nick.rutherford@gmail.com>\nAlexander Daniel <AlexanderRDaniel@gmail.com>\nHuizhe Wang <conciselove@outlook.com>\nManoj Krishnan <manoj.k@freshdesk.com>\n"
  },
  {
    "path": "htmlbeautifier.gemspec",
    "content": "require File.expand_path(\"../lib/htmlbeautifier/version\", __FILE__)\n\nspec = Gem::Specification.new do |s|\n  s.name              = \"htmlbeautifier\"\n  s.version           = HtmlBeautifier::VERSION::STRING\n  s.summary           = \"HTML/ERB beautifier\"\n  s.description       = \"A normaliser/beautifier for HTML that also understands embedded Ruby.\"\n  s.author            = \"Paul Battley\"\n  s.email             = \"pbattley@gmail.com\"\n  s.homepage          = \"http://github.com/threedaymonk/htmlbeautifier\"\n  s.license           = \"MIT\"\n\n  s.files             = %w(Rakefile README.md) + Dir.glob(\"{bin,test,lib}/**/*\")\n  s.executables       = Dir[\"bin/**\"].map { |f| File.basename(f) }\n\n  s.require_paths     = [\"lib\"]\n\n  s.required_ruby_version = '>= 2.6.0'\n\n  s.add_development_dependency \"rake\", \"~> 13\"\n  s.add_development_dependency \"rspec\", \"~> 3\"\n  s.add_development_dependency \"standard\", \"~> 1.33\"\n  s.add_development_dependency \"rubocop-rspec\", \"~> 2\"\n  s.add_development_dependency \"rubocop-rake\", \"~> 0.6\"\nend\n\n"
  },
  {
    "path": "lib/htmlbeautifier/builder.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"htmlbeautifier/parser\"\nrequire \"htmlbeautifier/ruby_indenter\"\n\nmodule HtmlBeautifier\n  class Builder\n    DEFAULT_OPTIONS = {\n      indent: \"  \",\n      initial_level: 0,\n      stop_on_errors: false,\n      keep_blank_lines: 0\n    }.freeze\n\n    def initialize(output, options = {})\n      options = DEFAULT_OPTIONS.merge(options)\n      @tab = options[:indent]\n      @stop_on_errors = options[:stop_on_errors]\n      @level = options[:initial_level]\n      @keep_blank_lines = options[:keep_blank_lines]\n      @new_line = false\n      @empty = true\n      @ie_cc_levels = []\n      @output = output\n      @embedded_indenter = RubyIndenter.new\n    end\n\n    private\n\n    def error(text)\n      return unless @stop_on_errors\n\n      raise text\n    end\n\n    def indent\n      @level += 1\n    end\n\n    def outdent\n      error \"Extraneous closing tag\" if @level == 0\n      @level = [@level - 1, 0].max\n    end\n\n    def emit(*strings)\n      strings_join = strings.join(\"\")\n      @output << \"\\n\" if @new_line && !@empty\n      @output << (@tab * @level) if @new_line && !strings_join.strip.empty?\n      @output << strings_join\n      @new_line = false\n      @empty = false\n    end\n\n    def new_line\n      @new_line = true\n    end\n\n    def embed(opening, code, closing)\n      lines = code.split(%r{\\n}).map(&:strip)\n      outdent if @embedded_indenter.outdent?(lines)\n      emit opening, code, closing\n      indent if @embedded_indenter.indent?(lines)\n    end\n\n    def foreign_block(opening, code, closing)\n      emit opening\n      emit_reindented_block_content code unless code.strip.empty?\n      emit closing\n    end\n\n    def emit_reindented_block_content(code)\n      lines = code.strip.split(%r{\\n})\n      indentation = foreign_block_indentation(code)\n\n      indent\n      new_line\n      lines.each do |line|\n        emit line.rstrip.sub(%r{^#{indentation}}, \"\")\n        new_line\n      end\n      outdent\n    end\n\n    def foreign_block_indentation(code)\n      code.split(%r{\\n}).find { |ln| !ln.strip.empty? }[%r{^\\s+}]\n    end\n\n    def preformatted_block(opening, content, closing)\n      new_line\n      emit opening, content, closing\n      new_line\n    end\n\n    def standalone_element(elem)\n      emit elem\n      new_line if elem =~ %r{^<br[^\\w]}\n    end\n\n    def close_element(elem)\n      outdent\n      emit elem\n    end\n\n    def close_block_element(elem)\n      close_element elem\n      new_line\n    end\n\n    def open_element(elem)\n      emit elem\n      indent\n    end\n\n    def open_block_element(elem)\n      new_line\n      open_element elem\n    end\n\n    def close_ie_cc(elem)\n      if @ie_cc_levels.empty?\n        error \"Unclosed conditional comment\"\n      else\n        @level = @ie_cc_levels.pop\n      end\n      emit elem\n    end\n\n    def open_ie_cc(elem)\n      emit elem\n      @ie_cc_levels.push @level\n      indent\n    end\n\n    def new_lines(*content)\n      blank_lines = content.first.scan(%r{\\n}).count - 1\n      blank_lines = [blank_lines, @keep_blank_lines].min\n      @output << (\"\\n\" * blank_lines)\n      new_line\n    end\n\n    alias_method :text, :emit\n  end\nend\n"
  },
  {
    "path": "lib/htmlbeautifier/html_parser.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"htmlbeautifier/parser\"\n\nmodule HtmlBeautifier\n  class HtmlParser < Parser\n    ELEMENT_CONTENT = %r{ (?:<%.*?%>|[^>])* }mx\n    HTML_VOID_ELEMENTS = %r{(?:\n      area | base | br | col | command | embed | hr | img | input | keygen |\n      link | meta | param | source | track | wbr\n    )}mix\n    HTML_BLOCK_ELEMENTS = %r{(?:\n      address | article | aside | audio | blockquote | canvas | dd | details |\n      dir | div | dl | dt | fieldset | figcaption | figure | footer | form |\n      h1 | h2 | h3 | h4 | h5 | h6 | header | hr | li | menu | noframes |\n      noscript | ol | p | pre | section | table | tbody | td | tfoot | th |\n      thead | tr | ul | video\n    )}mix\n\n    MAPPINGS = [\n      [%r{(<%-?=?)(.*?)(-?%>)}om,\n        :embed],\n      [%r{<!--\\[.*?\\]>}om,\n        :open_ie_cc],\n      [%r{<!\\[.*?\\]-->}om,\n        :close_ie_cc],\n      [%r{<!--.*?-->}om,\n        :standalone_element],\n      [%r{<!.*?>}om,\n        :standalone_element],\n      [%r{(<script#{ELEMENT_CONTENT}>)(.*?)(</script>)}omi,\n        :foreign_block],\n      [%r{(<style#{ELEMENT_CONTENT}>)(.*?)(</style>)}omi,\n        :foreign_block],\n      [%r{(<pre#{ELEMENT_CONTENT}>)(.*?)(</pre>)}omi,\n        :preformatted_block],\n      [%r{(<textarea#{ELEMENT_CONTENT}>)(.*?)(</textarea>)}omi,\n        :preformatted_block],\n      [%r{<#{HTML_VOID_ELEMENTS}(?: #{ELEMENT_CONTENT})?/?>}om,\n        :standalone_element],\n      [%r{</#{HTML_BLOCK_ELEMENTS}>}om,\n        :close_block_element],\n      [%r{<#{HTML_BLOCK_ELEMENTS}(?: #{ELEMENT_CONTENT})?>}om,\n        :open_block_element],\n      [%r{</#{ELEMENT_CONTENT}>}om,\n        :close_element],\n      [%r{<#{ELEMENT_CONTENT}[^/]>}om,\n        :open_element],\n      [%r{<[\\w\\-]+(?: #{ELEMENT_CONTENT})?/>}om,\n        :standalone_element],\n      [%r{(\\s*\\r?\\n\\s*)+}om,\n        :new_lines],\n      [%r{[^<\\n]+},\n        :text]\n    ].freeze\n\n    def initialize\n      super do |p|\n        MAPPINGS.each do |regexp, method|\n          p.map regexp, method\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/htmlbeautifier/parser.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"strscan\"\n\nmodule HtmlBeautifier\n  class Parser\n    def initialize\n      @maps = []\n      yield self if block_given?\n    end\n\n    def map(pattern, method)\n      @maps << [pattern, method]\n    end\n\n    def scan(subject, receiver)\n      @scanner = StringScanner.new(subject)\n      dispatch(receiver) until @scanner.eos?\n    end\n\n    def source_so_far\n      @scanner.string[0...@scanner.pos]\n    end\n\n    def source_line_number\n      [source_so_far.chomp.split(%r{\\n}).count, 1].max\n    end\n\n    private\n\n    def dispatch(receiver)\n      _, method = @maps.find { |pattern, _| @scanner.scan(pattern) }\n      raise \"Unmatched sequence\" unless method\n\n      receiver.__send__(method, *extract_params(@scanner))\n    rescue => e\n      raise \"#{e.message} on line #{source_line_number}\"\n    end\n\n    def extract_params(scanner)\n      return [scanner[0]] unless scanner[1]\n\n      params = []\n      i = 1\n      while scanner[i]\n        params << scanner[i]\n        i += 1\n      end\n      params\n    end\n  end\nend\n"
  },
  {
    "path": "lib/htmlbeautifier/ruby_indenter.rb",
    "content": "# frozen_string_literal: true\n\nmodule HtmlBeautifier\n  class RubyIndenter\n    INDENT_KEYWORDS = %w[if elsif else unless while until begin for case when].freeze\n    OUTDENT_KEYWORDS = %w[elsif else end when].freeze\n    RUBY_INDENT = %r{\n      ^ ( #{INDENT_KEYWORDS.join(\"|\")} )\\b\n      | \\b ( do | \\{ ) ( \\s* \\| [^|]+ \\| )? $\n    }xo\n    RUBY_OUTDENT = %r{ ^ ( #{OUTDENT_KEYWORDS.join(\"|\")} | \\} ) \\b }xo\n\n    def outdent?(lines)\n      lines.first =~ RUBY_OUTDENT\n    end\n\n    def indent?(lines)\n      lines.last =~ RUBY_INDENT\n    end\n  end\nend\n"
  },
  {
    "path": "lib/htmlbeautifier/version.rb",
    "content": "# frozen_string_literal: true\n\nmodule HtmlBeautifier # :nodoc:\n  module VERSION # :nodoc:\n    MAJOR = 1\n    MINOR = 4\n    TINY = 2\n\n    STRING = [MAJOR, MINOR, TINY].join(\".\")\n  end\nend\n"
  },
  {
    "path": "lib/htmlbeautifier.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"htmlbeautifier/builder\"\nrequire \"htmlbeautifier/html_parser\"\nrequire \"htmlbeautifier/version\"\n\nmodule HtmlBeautifier\n  #\n  # Returns a beautified HTML/HTML+ERB document as a String.\n  # html must be an object that responds to +#to_s+.\n  #\n  # Available options are:\n  # tab_stops - an integer for the number of spaces to indent, default 2.\n  # Deprecated: see indent.\n  # indent - what to indent with (\"  \", \"\\t\" etc.), default \"  \"\n  # stop_on_errors - raise an exception on a badly-formed document. Default\n  # is false, i.e. continue to process the rest of the document.\n  # initial_level - The entire output will be indented by this number of steps.\n  # Default is 0.\n  # keep_blank_lines - an integer for the number of consecutive empty lines\n  # to keep in output.\n  #\n  def self.beautify(html, options = {})\n    if options[:tab_stops]\n      options[:indent] = \" \" * options[:tab_stops]\n    end\n    String.new.tap { |output|\n      HtmlParser.new.scan html.to_s, Builder.new(output, options)\n    }\n  end\nend\n"
  },
  {
    "path": "spec/.rubocop.yml",
    "content": "inherit_from:\n  - ../.rubocop.yml\nrequire:\n  - rubocop-rspec\n\n# I can't always fit test data in 80 chars\nLayout/LineLength:\n  Enabled: false\n\nMetrics/MethodLength:\n  Enabled: false\n\n# I'd like to enable this, but there's a lot of % interpolation in the specs\nStyle/FormatStringToken:\n  Enabled: false\n\n# By its nature this is not a class or module\nRSpec/DescribeClass:\n  Exclude:\n    - executable_spec.rb\n\n# Pragmatic relaxation to avoid shelling out too often\nRSpec/MultipleExpectations:\n  Exclude:\n    - executable_spec.rb\n\n# To be revisited\nRSpec/ExampleLength:\n  Enabled: false\nRSpec/FilePath:\n  Enabled: false\n\n# Enable these new cops\nRSpec/ExcessiveDocstringSpacing:\n  Enabled: true\nRSpec/IdenticalEqualityAssertion:\n  Enabled: true\nRSpec/SubjectDeclaration:\n  Enabled: true\nRSpec/Rails/AvoidSetupHook:\n  Enabled: true\n"
  },
  {
    "path": "spec/behavior_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"htmlbeautifier\"\n\ndescribe HtmlBeautifier do\n  it \"ignores HTML fragments in embedded ERB\" do\n    source = code <<~HTML\n      <div>\n        <%= a[:b].gsub(\"\\n\",\"<br />\\n\") %>\n      </div>\n    HTML\n    expected = code <<~HTML\n      <div>\n        <%= a[:b].gsub(\"\\n\",\"<br />\\n\") %>\n      </div>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"allows < in an attribute\" do\n    source = code <<~HTML\n      <div ng-show=\"foo < 1\">\n      <p>Hello</p>\n      </div>\n    HTML\n    expected = code <<~HTML\n      <div ng-show=\"foo < 1\">\n        <p>Hello</p>\n      </div>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"allows > in an attribute\" do\n    source = code <<~HTML\n      <div ng-show=\"foo > 1\">\n      <p>Hello</p>\n      </div>\n    HTML\n    expected = code <<~HTML\n      <div ng-show=\"foo > 1\">\n        <p>Hello</p>\n      </div>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"indents within <script>\" do\n    source = code <<~HTML\n      <script>\n      function(f) {\n          g();\n          return 42;\n      }\n      </script>\n    HTML\n    expected = code <<~HTML\n      <script>\n        function(f) {\n            g();\n            return 42;\n        }\n      </script>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"does not indent blank lines in scripts\" do\n    source = \"<script>\\n  function(f) {\\n\\n  }\\n</script>\"\n    expected = \"<script>\\n  function(f) {\\n\\n  }\\n</script>\"\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"handles self-closing HTML fragments in javascript: <img /> (bug repro)\" do\n    source = code <<-ERB\n    <div class=\"<%= get_class %>\" ></div>\n    <script>\n      var warning_message = \"<%= confirm_data %>\";\n      $('#img').html('<img src=\"/myimg.jpg\" />')\n      $('#errors').html('<div class=\"alert alert-danger\" role=\"alert\">' + error_message + '</div>')\n    </script>\n    ERB\n\n    expect(described_class.beautify(source, stop_on_errors: true)).to eq(source)\n  end\n\n  it \"indents only the first line of code inside <script> or <style> and retains the other lines' indents relative to the first line\" do\n    source = code <<~HTML\n      <script>\n        function(f) {\n            g();\n            return 42;\n        }\n      </script>\n      <style>\n                    .foo{ margin: 0; }\n                    .bar{\n                      padding: 0;\n                      margin: 0;\n                    }\n      </style>\n      <style>\n        .foo{ margin: 0; }\n                    .bar{\n                      padding: 0;\n                      margin: 0;\n                    }\n      </style>\n    HTML\n    expected = code <<~HTML\n      <script>\n        function(f) {\n            g();\n            return 42;\n        }\n      </script>\n      <style>\n        .foo{ margin: 0; }\n        .bar{\n          padding: 0;\n          margin: 0;\n        }\n      </style>\n      <style>\n        .foo{ margin: 0; }\n                    .bar{\n                      padding: 0;\n                      margin: 0;\n                    }\n      </style>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"retains empty <script> and <style> blocks\" do\n    source = code <<~HTML\n      <script>\n\n      </script>\n      <style>\n\n      </style>\n    HTML\n    expected = code <<~HTML\n      <script></script>\n      <style></style>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"trims blank lines around scripts\" do\n    source = code <<~HTML\n      <script>\n\n        f();\n\n      </script>\n    HTML\n    expected = code <<~HTML\n      <script>\n        f();\n      </script>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"removes trailing space from script lines\" do\n    source = \"<script>\\n  f();  \\n</script>\"\n    expected = \"<script>\\n  f();\\n</script>\"\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"leaves empty scripts as they are\" do\n    source = %(<script src=\"/foo.js\" type=\"text/javascript\" charset=\"utf-8\"></script>)\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"removes whitespace from script tags containing only whitespace\" do\n    source = \"<script>\\n</script>\"\n    expected = \"<script></script>\"\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"ignores case of <script> tag\" do\n    source = code <<~HTML\n      <SCRIPT>\n\n      // code\n\n      </SCRIPT>\n    HTML\n    expected = code <<~HTML\n      <SCRIPT>\n        // code\n      </SCRIPT>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"indents within <style>\" do\n    source = code <<~HTML\n      <style>\n      .foo{ margin: 0; }\n      .bar{\n        padding: 0;\n        margin: 0;\n      }\n      </style>\n    HTML\n    expected = code <<~HTML\n      <style>\n        .foo{ margin: 0; }\n        .bar{\n          padding: 0;\n          margin: 0;\n        }\n      </style>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"trims blank lines around styles\" do\n    source = code <<~HTML\n      <style>\n\n        .foo{ margin: 0; }\n\n      </style>\n    HTML\n    expected = code <<~HTML\n      <style>\n        .foo{ margin: 0; }\n      </style>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"removes trailing space from style lines\" do\n    source = \"<style>\\n  .foo{ margin: 0; }  \\n</style>\"\n    expected = \"<style>\\n  .foo{ margin: 0; }\\n</style>\"\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"ignores case of <style> tag\" do\n    source = code <<~HTML\n      <STYLE>\n\n      .foo{ margin: 0; }\n\n      </STYLE>\n    HTML\n    expected = code <<~HTML\n      <STYLE>\n        .foo{ margin: 0; }\n      </STYLE>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"indents <div>s containing standalone elements\" do\n    source = code <<~HTML\n      <div>\n      <div>\n      <img src=\"foo\" alt=\"\" />\n      </div>\n      <div>\n      <img src=\"foo\" alt=\"\" />\n      </div>\n      </div>\n    HTML\n    expected = code <<~HTML\n      <div>\n        <div>\n          <img src=\"foo\" alt=\"\" />\n        </div>\n        <div>\n          <img src=\"foo\" alt=\"\" />\n        </div>\n      </div>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"does not break line on embedded code within <script> opening tag\" do\n    source = %(<script src=\"<%= path %>\" type=\"text/javascript\"></script>)\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"does not break line on embedded code within normal element\" do\n    source = %(<img src=\"<%= path %>\" alt=\"foo\" />)\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"outdents else\" do\n    source = code <<~ERB\n      <% if @x %>\n      Foo\n      <% else %>\n      Bar\n      <% end %>\n    ERB\n    expected = code <<~ERB\n      <% if @x %>\n        Foo\n      <% else %>\n        Bar\n      <% end %>\n    ERB\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"indents with hyphenated ERB tags\" do\n    source = code <<~ERB\n      <%- if @x -%>\n      <%- @ys.each do |y| -%>\n      <p>Foo</p>\n      <%- end -%>\n      <%- elsif @z -%>\n      <hr />\n      <%- end -%>\n    ERB\n    expected = code <<~ERB\n      <%- if @x -%>\n        <%- @ys.each do |y| -%>\n          <p>Foo</p>\n        <%- end -%>\n      <%- elsif @z -%>\n        <hr />\n      <%- end -%>\n    ERB\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"indents case statements\" do\n    source = code <<~ERB\n      <div>\n        <% case @x %>\n        <% when :a %>\n        a\n        <% when :b %>\n        b\n        <% else %>\n        c\n        <% end %>\n      </div>\n    ERB\n    expected = code <<~ERB\n      <div>\n        <% case @x %>\n        <% when :a %>\n          a\n        <% when :b %>\n          b\n        <% else %>\n          c\n        <% end %>\n      </div>\n    ERB\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"stays indented within <details> with Boolean attribute handled by ERB\" do\n    source = code <<~ERB\n      <details <%= \"open\" if opened %>>\n        <summary>Hello</summary>\n        <div>\n          <table>\n            <% items.each do |item| %>\n              <tr>\n                <td>\n                  <code><%= item %></code>\n                </td>\n              </tr>\n            <% end %>\n          </table>\n        </div>\n      </details>\n    ERB\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"does not indent after comments\" do\n    source = code <<~HTML\n      <!-- This is a comment -->\n      <!-- So is this -->\n    HTML\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"does not indent one-line IE conditional comments\" do\n    source = code <<~HTML\n      <!--[if lt IE 7]><html lang=\"en-us\" class=\"ie6\"><![endif]-->\n      <!--[if IE 7]><html lang=\"en-us\" class=\"ie7\"><![endif]-->\n      <!--[if IE 8]><html lang=\"en-us\" class=\"ie8\"><![endif]-->\n      <!--[if gt IE 8]><!--><html lang=\"en-us\"><!--<![endif]-->\n        <body>\n        </body>\n      </html>\n    HTML\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"indents inside IE conditional comments\" do\n    source = code <<~HTML\n      <!--[if IE 6]>\n      <link rel=\"stylesheet\" href=\"/stylesheets/ie6.css\" type=\"text/css\" />\n      <![endif]-->\n      <!--[if IE 5]>\n      <link rel=\"stylesheet\" href=\"/stylesheets/ie5.css\" type=\"text/css\" />\n      <![endif]-->\n    HTML\n    expected = code <<~HTML\n      <!--[if IE 6]>\n        <link rel=\"stylesheet\" href=\"/stylesheets/ie6.css\" type=\"text/css\" />\n      <![endif]-->\n      <!--[if IE 5]>\n        <link rel=\"stylesheet\" href=\"/stylesheets/ie5.css\" type=\"text/css\" />\n      <![endif]-->\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"does not indent after doctype\" do\n    source = code <<~HTML\n      <!DOCTYPE html>\n      <html>\n      </html>\n    HTML\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"does not indent after void HTML elements\" do\n    source = code <<~HTML\n      <meta>\n      <input id=\"id\">\n      <br>\n    HTML\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"ignores case of void elements\" do\n    source = code <<~HTML\n      <META>\n      <INPUT id=\"id\">\n      <BR>\n    HTML\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"does not treat <colgroup> as standalone\" do\n    source = code <<~HTML\n      <colgroup>\n        <col style=\"width: 50%;\">\n      </colgroup>\n    HTML\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"does not modify content of <pre>\" do\n    source = code <<~HTML\n      <div>\n        <pre>   Preformatted   text\n\n                should  <em>not  be </em>\n                      modified,\n                ever!\n\n        </pre>\n      </div>\n    HTML\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"adds a single newline after block elements\" do\n    source = code <<~HTML\n      <section><h1>Title</h1><p>Lorem <em>ipsum</em></p>\n      <ol>\n        <li>First</li><li>Second</li></ol>\n\n\n      </section>\n    HTML\n    expected = code <<~HTML\n      <section>\n        <h1>Title</h1>\n        <p>Lorem <em>ipsum</em></p>\n        <ol>\n          <li>First</li>\n          <li>Second</li>\n        </ol>\n      </section>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"does not modify content of <textarea>\" do\n    source = code <<~HTML\n      <div>\n        <textarea>   Preformatted   text\n\n                should  <em>not  be </em>\n                      modified,\n                ever!\n\n        </textarea>\n      </div>\n    HTML\n    expect(described_class.beautify(source)).to eq(source)\n  end\n\n  it \"adds newlines around <pre>\" do\n    source = %(<section><pre>puts \"Allons-y!\"</pre></section>)\n    expected = code <<~HTML\n      <section>\n        <pre>puts \"Allons-y!\"</pre>\n      </section>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"adds newline after <br>\" do\n    source = %(<p>Lorem ipsum<br>dolor sit<br />amet,<br/>consectetur.</p>)\n    expected = code <<~HTML\n      <p>Lorem ipsum<br>\n        dolor sit<br />\n        amet,<br/>\n        consectetur.</p>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"indents after control expressions without optional `do` keyword\" do\n    source = code <<~ERB\n      <% for value in list %>\n      Lorem ipsum\n      <% end %>\n      <% until something %>\n      Lorem ipsum\n      <% end %>\n      <% while something_else %>\n      Lorem ipsum\n      <% end %>\n    ERB\n    expected = code <<~ERB\n      <% for value in list %>\n        Lorem ipsum\n      <% end %>\n      <% until something %>\n        Lorem ipsum\n      <% end %>\n      <% while something_else %>\n        Lorem ipsum\n      <% end %>\n    ERB\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"indents general self-closing tags\" do\n    source = code <<~HTML\n      <div>\n      <svg>\n      <path d=\"M150 0 L75 200 L225 200 Z\" />\n      <circle cx=\"50\" cy=\"50\" r=\"40\" />\n      </svg>\n      <br>\n      <br/>\n      <p>\n      <foo />\n      </p>\n      </div>\n    HTML\n    expected = code <<~HTML\n      <div>\n        <svg>\n          <path d=\"M150 0 L75 200 L225 200 Z\" />\n          <circle cx=\"50\" cy=\"50\" r=\"40\" />\n        </svg>\n        <br>\n        <br/>\n        <p>\n          <foo />\n        </p>\n      </div>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"removes excess indentation on next line after text\" do\n    source = code <<~HTML\n      Lorem ipsum\n                      <br>\n      Lorem ipsum\n                      <em>\n        Lorem ipsum\n                      </em>\n    HTML\n    expected = code <<~HTML\n      Lorem ipsum\n      <br>\n      Lorem ipsum\n      <em>\n        Lorem ipsum\n      </em>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  it \"indents subsequent lines of multiline text\" do\n    source = code <<~HTML\n      <p>\n      Lorem\n      Lorem\n      Lorem\n      </p>\n    HTML\n    expected = code <<~HTML\n      <p>\n        Lorem\n        Lorem\n        Lorem\n      </p>\n    HTML\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  context \"when keep_blank_lines is 0\" do\n    it \"removes all blank lines\" do\n      source = code <<~HTML\n        <h1>Lorem</h1>\n\n\n\n        <p>Ipsum</p>\n      HTML\n      expected = code <<~HTML\n        <h1>Lorem</h1>\n        <p>Ipsum</p>\n      HTML\n      expect(described_class.beautify(source, keep_blank_lines: 0)).to eq(expected)\n    end\n  end\n\n  context \"when keep_blank_lines is 1\" do\n    it \"removes all blank lines but 1\" do\n      source = code <<~HTML\n        <h1>Lorem</h1>\n\n\n\n        <p>Ipsum</p>\n      HTML\n      expected = code <<~HTML\n        <h1>Lorem</h1>\n\n        <p>Ipsum</p>\n      HTML\n      expect(described_class.beautify(source, keep_blank_lines: 1)).to eq(expected)\n    end\n\n    it \"does not add blank lines\" do\n      source = code <<~HTML\n        <h1>Lorem</h1>\n        <div>\n          Ipsum\n          <p>dolor</p>\n        </div>\n      HTML\n      expect(described_class.beautify(source, keep_blank_lines: 1)).to eq(source)\n    end\n\n    it \"does not indent blank lines\" do\n      source = code <<~HTML\n        <div>\n          Ipsum\n\n\n          <p>dolor</p>\n        </div>\n      HTML\n      expected = code <<~HTML\n        <div>\n          Ipsum\n\n          <p>dolor</p>\n        </div>\n      HTML\n      expect(described_class.beautify(source, keep_blank_lines: 1)).to eq(expected)\n    end\n  end\n\n  context \"when keep_blank_lines is 2\" do\n    it \"removes all blank lines but 2\" do\n      source = code <<~HTML\n        <h1>Lorem</h1>\n\n\n\n        <p>Ipsum</p>\n      HTML\n      expected = code <<~HTML\n        <h1>Lorem</h1>\n\n\n        <p>Ipsum</p>\n      HTML\n      expect(described_class.beautify(source, keep_blank_lines: 2)).to eq(expected)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/documents_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"htmlbeautifier\"\n\ndescribe HtmlBeautifier do\n  it \"correctly indents mixed document\" do\n    source = code <<~ERB\n      <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n      <html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n      <head>\n      <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n      <script src=\"/javascripts/prototype.js\" type=\"text/javascript\"></script>\n      <link rel=\"stylesheet\" type=\"text/css\" href=\"/stylesheets/screen.css\" media=\"screen\"/>\n      <!--[if IE 6]>\n      <link rel=\"stylesheet\" href=\"/stylesheets/screen_ie6.css\" type=\"text/css\" />\n      <![endif]-->\n      <title>Title Goes Here</title>\n      <script type=\"text/javascript\" charset=\"utf-8\">\n      doSomething();\n      </script>\n      </head>\n      <body>\n      <div id=\"something\">\n      <h1>\n      Heading 1\n      </h1>\n      </div>\n      <div id=\"somethingElse\"><p>Lorem Ipsum</p>\n      <% if @x %>\n      <% @ys.each do |y| %>\n      <p>\n      <%= h y %>\n      </p>\n      <% end %>\n      <% elsif @z %>\n      <hr />\n      <% end %>\n      </div>\n      <table>\n      <colgroup>\n      <col style=\"width: 50%;\">\n      <col style=\"width: 50%;\">\n      </colgroup>\n      <tbody>\n      <tr><td>First column</td></tr><tr>\n      <td>Second column</td></tr>\n      </tbody>\n      </table>\n      <custom-tag attr1=\"\" />\n      </body>\n      </html>\n    ERB\n    expected = code <<~ERB\n      <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n      <html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n        <head>\n          <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n          <script src=\"/javascripts/prototype.js\" type=\"text/javascript\"></script>\n          <link rel=\"stylesheet\" type=\"text/css\" href=\"/stylesheets/screen.css\" media=\"screen\"/>\n          <!--[if IE 6]>\n            <link rel=\"stylesheet\" href=\"/stylesheets/screen_ie6.css\" type=\"text/css\" />\n          <![endif]-->\n          <title>Title Goes Here</title>\n          <script type=\"text/javascript\" charset=\"utf-8\">\n            doSomething();\n          </script>\n        </head>\n        <body>\n          <div id=\"something\">\n            <h1>\n              Heading 1\n            </h1>\n          </div>\n          <div id=\"somethingElse\">\n            <p>Lorem Ipsum</p>\n            <% if @x %>\n              <% @ys.each do |y| %>\n                <p>\n                  <%= h y %>\n                </p>\n              <% end %>\n            <% elsif @z %>\n              <hr />\n            <% end %>\n          </div>\n          <table>\n            <colgroup>\n              <col style=\"width: 50%;\">\n              <col style=\"width: 50%;\">\n            </colgroup>\n            <tbody>\n              <tr>\n                <td>First column</td>\n              </tr>\n              <tr>\n                <td>Second column</td>\n              </tr>\n            </tbody>\n          </table>\n          <custom-tag attr1=\"\" />\n        </body>\n      </html>\n    ERB\n\n    expect(described_class.beautify(source)).to eq(expected)\n  end\n\n  context \"when stop_on_errors is true\" do\n    it \"raises an error with the source line of an illegal closing tag\" do\n      expect {\n        source = \"<html>\\n</html>\\n</html>\"\n        described_class.beautify(source, stop_on_errors: true)\n      }.to raise_error(RuntimeError, \"Extraneous closing tag on line 3\")\n    end\n  end\n\n  context \"when stop_on_errors is false\" do\n    it \"processes the rest of the document after the errant closing tag\" do\n      source = code <<~HTML\n        </head>\n        <body>\n        <div>\n        text\n        </div>\n        </body>\n      HTML\n      expected = code <<~HTML\n        </head>\n        <body>\n          <div>\n            text\n          </div>\n        </body>\n      HTML\n      expect(described_class.beautify(source, stop_on_errors: false))\n        .to eq(expected)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/executable_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"shellwords\"\nrequire \"fileutils\"\nrequire \"open3\"\n\ndescribe \"bin/htmlbeautifier\" do\n  before do\n    FileUtils.mkdir_p path_to(\"tmp\")\n  end\n\n  def write(path, content)\n    File.write(path, content)\n  end\n\n  def read(path)\n    File.read(path)\n  end\n\n  def path_to(*partial)\n    File.join(File.expand_path(\"..\", __dir__), *partial)\n  end\n\n  def command\n    \"ruby -I%s %s\" % [\n      escape(path_to(\"lib\")),\n      escape(path_to(\"bin\", \"htmlbeautifier\"))\n    ]\n  end\n\n  def escape(str)\n    Shellwords.escape(str)\n  end\n\n  it \"beautifies a file in place\" do\n    input = \"<p>\\nfoo\\n</p>\"\n    expected = \"<p>\\n  foo\\n</p>\\n\"\n    path = path_to(\"tmp\", \"in-place.html\")\n    write path, input\n\n    system \"%s %s\" % [command, escape(path)]\n\n    expect(read(path)).to eq(expected)\n  end\n\n  it \"beautifies a file from stdin to stdout\" do\n    input = \"<p>\\nfoo\\n</p>\"\n    expected = \"<p>\\n  foo\\n</p>\\n\"\n    in_path = path_to(\"tmp\", \"input.html\")\n    out_path = path_to(\"tmp\", \"output.html\")\n    write in_path, input\n\n    system \"%s < %s > %s\" % [command, escape(in_path), escape(out_path)]\n\n    expect(read(out_path)).to eq(expected)\n  end\n\n  it \"displays which files would fail with --lint-only flag\" do\n    good_input = \"<p></p>\\n\"\n    good_path = path_to(\"tmp\", \"good.html\")\n    write(good_path, good_input)\n\n    bad_input = \"<div><p></p></div>\\n\"\n    bad_path = path_to(\"tmp\", \"bad.html\")\n    write(bad_path, bad_input)\n\n    expected_message = \"Lint failed - files would be modified:\\n<redacted>/tmp/bad.html\\n\"\n\n    _stdout, stderr, status = Open3.capture3(\n      \"%s %s %s --lint-only\" % [command, escape(good_path), escape(bad_path)]\n    )\n\n    stderr.sub!(%r{/.*tmp/}, \"<redacted>/tmp/\")\n\n    expect(status.exitstatus).to eq(1)\n    expect(stderr).to eq(expected_message)\n  end\n\n  it \"does not modify files with --lint-only flag\" do\n    good_input = \"<p></p>\\n\"\n    good_path = path_to(\"tmp\", \"good.html\")\n    write(good_path, good_input)\n\n    bad_input = \"<div><p></p></div>\\n\"\n    bad_path = path_to(\"tmp\", \"bad.html\")\n    write(bad_path, bad_input)\n\n    Open3.capture3(\n      \"%s %s %s --lint-only\" % [command, escape(good_path), escape(bad_path)]\n    )\n\n    expect(read(good_path)).to eq(good_input)\n    expect(read(bad_path)).to eq(bad_input)\n  end\n\n  it \"allows a configurable number of tab stops\" do\n    input = \"<p>\\nfoo\\n</p>\"\n    expected = \"<p>\\n   foo\\n</p>\\n\"\n    path = path_to(\"tmp\", \"in-place.html\")\n    write path, input\n\n    system \"%s --tab-stops=3 %s\" % [command, escape(path)]\n\n    expect(read(path)).to eq(expected)\n  end\n\n  it \"allows indentation with tab instead of spaces\" do\n    input = \"<p>\\nfoo\\n</p>\"\n    expected = \"<p>\\n\\tfoo\\n</p>\\n\"\n    path = path_to(\"tmp\", \"in-place.html\")\n    write path, input\n\n    system \"%s --tab %s\" % [command, escape(path)]\n\n    expect(read(path)).to eq(expected)\n  end\n\n  it \"allows an initial indentation level\" do\n    input = \"<p>\\nfoo\\n</p>\"\n    expected = \"      <p>\\n        foo\\n      </p>\\n\"\n    path = path_to(\"tmp\", \"in-place.html\")\n    write path, input\n\n    system \"%s --indent-by 3 %s\" % [command, escape(path)]\n\n    expect(read(path)).to eq(expected)\n  end\n\n  it \"ignores closing tag errors by default\" do\n    input = \"</p>\\n\"\n    expected = \"</p>\\n\"\n    path = path_to(\"tmp\", \"in-place.html\")\n    write path, input\n\n    status = system(\"%s %s\" % [command, escape(path)])\n\n    expect(read(path)).to eq(expected)\n    expect(status).to be_truthy\n  end\n\n  it \"raises an exception on closing tag errors with --stop-on-errors\" do\n    input = \"</p>\\n\"\n    path = path_to(\"tmp\", \"in-place.html\")\n    write path, input\n\n    status = system(\"%s --stop-on-errors %s 2>/dev/null\" % [command, escape(path)])\n\n    expect(status).to be_falsey\n  end\n\n  it \"allows a configurable number of consecutive blank lines\" do\n    input = \"<h1>foo</h1>\\n\\n\\n\\n\\n<p>bar</p>\\n\"\n    expected = \"<h1>foo</h1>\\n\\n\\n<p>bar</p>\\n\"\n    path = path_to(\"tmp\", \"in-place.html\")\n    write path, input\n\n    system \"%s --keep-blank-lines=2 %s\" % [command, escape(path)]\n\n    expect(read(path)).to eq(expected)\n  end\nend\n"
  },
  {
    "path": "spec/parser_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"htmlbeautifier/parser\"\n\ndescribe HtmlBeautifier::Parser do\n  let(:receiver_class) {\n    Class.new do\n      attr_reader :sequence\n\n      def initialize\n        @sequence = []\n      end\n\n      def method_missing(method, *params)\n        @sequence << [method, params]\n      end\n\n      def respond_to_missing?\n        true\n      end\n    end\n  }\n\n  it \"dispatches matching sequence\" do\n    receiver = receiver_class.new\n    parser = described_class.new { |p|\n      p.map %r{foo}, :foo\n      p.map %r{bar\\s*}, :bar\n      p.map %r{\\s+}, :whitespace\n    }\n    parser.scan(\"foo bar \", receiver)\n    expected = [[:foo, [\"foo\"]], [:whitespace, [\" \"]], [:bar, [\"bar \"]]]\n    expect(receiver.sequence).to eq(expected)\n  end\n\n  it \"sends parenthesized components as separate parameters\" do\n    receiver = receiver_class.new\n    parser = described_class.new { |p|\n      p.map %r{(foo)\\((.*?)\\)}, :foo\n    }\n    parser.scan(\"foo(bar)\", receiver)\n    expected = [[:foo, %w[foo bar]]]\n    expect(receiver.sequence).to eq(expected)\n  end\n\n  context \"when tracking source lines\" do\n    let(:source_tracking_receiver_class) {\n      Class.new(receiver_class) do\n        attr_reader :sources_so_far\n        attr_reader :source_line_numbers\n\n        def initialize(parser)\n          @sources_so_far = []\n          @source_line_numbers = []\n          @parser = parser\n          super()\n        end\n\n        def append_new_source_so_far(*)\n          @sources_so_far << @parser.source_so_far\n        end\n\n        def append_new_source_line_number(*)\n          @source_line_numbers << @parser.source_line_number\n        end\n      end\n    }\n\n    it \"gives source so far\" do\n      parser = described_class.new { |p|\n        p.map %r{(M+)}m, :append_new_source_so_far\n        p.map %r{([\\s\\n]+)}m, :space_or_newline\n      }\n      receiver = source_tracking_receiver_class.new(parser)\n      parser.scan(\"M MM MMM\", receiver)\n      expect(receiver.sources_so_far).to eq([\"M\", \"M MM\", \"M MM MMM\"])\n    end\n\n    it \"gives source line number\" do\n      parser = described_class.new { |p|\n        p.map %r{(M+)}m, :append_new_source_line_number\n        p.map %r{([\\s\\n]+)}m, :space_or_newline\n      }\n      receiver = source_tracking_receiver_class.new(parser)\n      parser.scan(\"M \\n\\nMM\\nMMM\", receiver)\n      expect(receiver.source_line_numbers).to eq([1, 3, 4])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule HtmlBeautifierSpecUtilities\n  def code(str)\n    str = str.gsub(%r{\\A\\n|\\n\\s*\\Z}, \"\")\n    indentation = str[%r{\\A +}]\n    lines = str.split(%r{\\n})\n    lines.map { |line| line.sub(%r{^#{indentation}}, \"\") }.join(\"\\n\")\n  end\nend\n\nRSpec.configure do |config|\n  config.include HtmlBeautifierSpecUtilities\nend\n"
  }
]