\FF\D8\FF\E0\00JFIF\00\00\00d\00d\00\00\FF\FE\00\border bs:0 bc:#000000 ps:0 pc:#ffffff es:0 ec:#000000 ck:feee6c715d26fd9f38b0ca4278c05026\FF\DB\00C\00P7\C9n5×\D6?\BDê\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7 \8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\ED\DF\E7Ms\FB\F1\8E\B3\FA\EA\E8\E6(\883zs\F2_\8DFk\8Bh \00\8C\DCw\D3R\B5+6X\BA\B2\C4j\AB0\B4\FCMw\C2I\8E\9B\E3\A9~9u\FA\D3l\80\C8%p\EE\FDn2€ \00 $\FEj\C4e\A9\DB\~\95\A7\A5\80EK\BB\8DDsP\00@@AD'k\CF\E8\DB\D2(\80\9AK\D3\85\D6lb\F2\BA\8C*\80\00)\95 59\A3R:\F3\CE"\B6\80\88\00\00i1u4ê\E9\F2á\A6\A2\FACM\93WMb*\E0*\00\00\00\00\00(\A8\80\00\00\80\00\00\00\00\00\00\00\00\00\FF\D9 C/// File Manager

File Manager

Path: /proc/self/root/opt/chef/embedded/lib/ruby/gems/2.7.0/gems/strings-0.1.8/lib/strings/

Viewing File: wrap.rb

# frozen_string_literal: true

require 'strings-ansi'
require 'unicode/display_width'
require 'unicode_utils/each_grapheme'

require_relative 'fold'

module Strings
  module Wrap
    DEFAULT_WIDTH = 80
    NEWLINE = "\n".freeze
    SPACE = " ".freeze
    LINE_BREAK = %r{\r\n|\r|\n}.freeze
    LINE_BREAKS = "\r\n+|\r+|\n+".freeze

    # Wrap a text into lines no longer than wrap_at length.
    # Preserves existing lines and existing word boundaries.
    #
    # @example
    #   Strings::Wrap.wrap("Some longish text", 8)
    #   # => "Some \nlongish \ntext"
    #
    # @api public
    def wrap(text, wrap_at = DEFAULT_WIDTH, separator: nil)
      if text.scan(/[[:print:]]/).length < wrap_at.to_i || wrap_at.to_i.zero?
        return text
      end
      ansi_stack = []
      sep = separator || text[LINE_BREAK] || NEWLINE
      text.split(%r{#{LINE_BREAKS}}, -1).map do |paragraph|
        format_paragraph(paragraph, wrap_at, ansi_stack)
      end * sep
    end
    module_function :wrap

    # Format paragraph to be maximum of wrap_at length
    #
    # @param [String] paragraph
    #   the paragraph to format
    # @param [Integer] wrap_at
    #   the maximum length to wrap the paragraph
    #
    # @return [Array[String]]
    #   the wrapped lines
    #
    # @api private
    def format_paragraph(paragraph, wrap_at, ansi_stack)
      cleared_para = Fold.fold(paragraph)
      lines = []
      line  = []
      word  = []
      ansi  = []
      ansi_matched = false
      word_length = 0
      line_length = 0
      char_length = 0 # visible char length
      text_length = display_width(cleared_para)
      total_length = 0

      UnicodeUtils.each_grapheme(cleared_para) do |char|
        # we found ansi let's consume
        if char == Strings::ANSI::CSI || ansi.length > 0
          ansi << char
          if Strings::ANSI.only_ansi?(ansi.join)
            ansi_matched = true
          elsif ansi_matched
            ansi_stack << [ansi[0...-1].join, line_length + word_length]
            ansi_matched = false

            if ansi.last == Strings::ANSI::CSI
              ansi = [ansi.last]
            else
              ansi = []
            end
          end
          next if ansi.length > 0
        end

        char_length = display_width(char)
        total_length += char_length
        if line_length + word_length + char_length <= wrap_at
          if char == SPACE || total_length == text_length
            line << word.join + char
            line_length += word_length + char_length
            word = []
            word_length = 0
          else
            word << char
            word_length += char_length
          end
          next
        end

        if char == SPACE # ends with space
          lines << insert_ansi(line.join, ansi_stack)
          line = []
          line_length = 0
          word << char
          word_length += char_length
        elsif word_length + char_length <= wrap_at
          lines << insert_ansi(line.join, ansi_stack)
          line = [word.join + char]
          line_length = word_length + char_length
          word = []
          word_length = 0
        else # hyphenate word - too long to fit a line
          lines << insert_ansi(word.join, ansi_stack)
          line_length = 0
          word = [char]
          word_length = char_length
        end
      end
      lines << insert_ansi(line.join, ansi_stack) unless line.empty?
      lines << insert_ansi(word.join, ansi_stack) unless word.empty?
      lines
    end
    module_function :format_paragraph

    # Insert ANSI code into string
    #
    # Check if there are any ANSI states, if present
    # insert ANSI codes at given positions unwinding the stack.
    #
    # @param [String] string
    #   the string to insert ANSI codes into
    #
    # @param [Array[Array[String, Integer]]] ansi_stack
    #   the ANSI codes to apply
    #
    # @return [String]
    #
    # @api private
    def insert_ansi(string, ansi_stack = [])
      return string if ansi_stack.empty?
      return string if string.empty?

      new_stack = []
      output          = string.dup
      length          = string.size
      matched_reset   = false
      ansi_reset      = Strings::ANSI::RESET

      # Reversed so that string index don't count ansi
      ansi_stack.reverse_each do |ansi|
        if ansi[0] =~ /#{Regexp.quote(ansi_reset)}/
          matched_reset = true
          output.insert(ansi[1], ansi_reset)
          next
        elsif !matched_reset # ansi without reset
          matched_reset = false
          new_stack << ansi # keep the ansi
          next if ansi[1] == length
          output.insert(-1, ansi_reset) # add reset at the end
        end

        output.insert(ansi[1], ansi[0])
      end

      ansi_stack.replace(new_stack)

      output
    end
    module_function :insert_ansi

    # Visible width of a string
    #
    # @api private
    def display_width(string)
      Unicode::DisplayWidth.of(Strings::ANSI.sanitize(string))
    end
    module_function :display_width
  end # Wrap
end # Strings