<commit_before>require 'tempfile'

module Bashcov
  # This class manages +xtrace+ output.
  #
  # @see Runner
  class Xtrace
    # @param [Array] output Array of output lines.
    # @raise [ArgumentError] if the given +output+ is not an array
    def initialize
      @xtrace_file = Tempfile.new 'xtrace_output'
    end

    def file_descriptor
      @xtrace_file.to_i
    end

    # Parses xtrace output and computes coverage
    # @raise [RuntimeError] on invalid files
    # @return [Hash] Hash of executed files with coverage information
    def files
      files = {}

      @xtrace_file.rewind
      @xtrace_file.read.each_line do |line|
        match = line.match(self.class.line_regexp)
        next if match.nil? # multiline instruction

        filename = File.expand_path(match[:filename], Bashcov.root_directory)
        next if File.directory? filename
        raise "#{filename} is not a file" unless File.file? filename

        lineno = match[:lineno].to_i
        lineno -= 1 if lineno > 0

        files[filename] ||= Bashcov.coverage_array(filename)

        files[filename][lineno] += 1
      end
      @xtrace_file.close

      files
    end

    def self.is_valid? line
      line =~ line_regexp
    end

    # @see http://www.gnu.org/software/bash/manual/bashref.html#index-PS4
    # @return [String] +PS4+ variable used for xtrace output
    def self.ps4
      # We use a forward slash as delimiter since it's the only forbidden
      # character in filenames on Unix and Windows.

      # http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
      %Q{#{prefix}$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")/${LINENO} BASHCOV: }
    end

  private

    def self.prefix
      # Note that the first caracter (+) will be repeated to indicate the
      # nesting level (see depth_character).
      '+BASHCOV> '
    end

    def self.depth_character
      Regexp.escape(prefix[0])
    end

    def self.line_regexp
      /\A#{depth_character}+#{prefix[1..-1]}(?<filename>.+)\/(?<lineno>\d+) BASHCOV: /
    end<commit_msg>Update documentation<commit_after>require 'tempfile'

module Bashcov
  # This class manages +xtrace+ output.
  #
  # @see Runner
  class Xtrace
    # Creates a temporary file for xtrace output
    def initialize
      @xtrace_file = Tempfile.new 'xtrace_output'
    end

    # @return [Fixnum] File descriptor of the output file
    def file_descriptor
      @xtrace_file.to_i
    end

    # Parses xtrace output and computes coverage
    # @raise [RuntimeError] on invalid files
    # @return [Hash] Hash of executed files with coverage information
    def files
      files = {}

      @xtrace_file.rewind
      @xtrace_file.read.each_line do |line|
        match = line.match(self.class.line_regexp)
        next if match.nil? # multiline instruction

        filename = File.expand_path(match[:filename], Bashcov.root_directory)
        next if File.directory? filename
        raise "#{filename} is not a file" unless File.file? filename

        lineno = match[:lineno].to_i
        lineno -= 1 if lineno > 0

        files[filename] ||= Bashcov.coverage_array(filename)

        files[filename][lineno] += 1
      end
      @xtrace_file.close

      files
    end

    # @param [String] line A string
    # @return [boolean] Whether the given line is xtrace output
    def self.is_valid? line
      line =~ line_regexp
    end

    # @see http://www.gnu.org/software/bash/manual/bashref.html#index-PS4
    # @return [String] +PS4+ variable used for xtrace output
    def self.ps4
      # We use a forward slash as delimiter since it's the only forbidden
      # character in filenames on Unix and Windows.

      # http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
      %Q{#{prefix}$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")/${LINENO} BASHCOV: }
    end

  private

    def self.prefix
      # Note that the first caracter (+) will be repeated to indicate the
      # nesting level (see depth_character).
      '+BASHCOV> '
    end

    def self.depth_character
      Regexp.escape(prefix[0])
    end

    def self.line_regexp
      /\A#{depth_character}+#{prefix[1..-1]}(?<filename>.+)\/(?<lineno>\d+) BASHCOV: /
    end
{'fraction_non_alphanumeric': 0.11023622047244094, 'fraction_numerical': 0.007410838351088467, 'mean_word_length': 2.700942587832048, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7620472', 'n_tokens_mistral': 1428, 'n_tokens_neox': 1341, 'n_words': 370}