# frozen_string_literal: true module Vedeu module DSL # Wrap or prune a text value based on given options when building # views. # # @api private # class Wordwrap include Vedeu::Common # @param (see #initialize) # @return (see #content) def self.for(text, options = {}) new(text, options).content end # Returns a new instance of Vedeu::DSL::Wordwrap. # # @param text [String] # @param options [Hash] # @option options ellipsis [String] See {#ellipsis}. # @option options mode [Symbol] See {#mode}. # @option options name [String|Symbol] See {#name}. # @option options width [Fixnum] See {#width}. # @return [Vedeu::DSL::Wordwrap] def initialize(text, options = {}) @text = text @options = defaults.merge!(options) end # @return [Vedeu::Views::Lines] def content case mode when :prune then to_line_objects(prune) when :wrap then to_line_objects(wrap) else to_line_objects(split_lines) end end # @return [Array|String] def prune return text if text.size <= pruned_width if split_lines.size > 1 split_lines.reduce([]) { |a, e| a << ellipsis_string(e) } else ellipsis_string(text) end end # @return [String] def wrap processed = [] split_lines.map do |unprocessed| line_length = 0 reformatted = [] unprocessed.split(/\s/).map do |word| word_length = word.length + 1 if (line_length += word_length) >= width line_length = word_length processed << reformatted reformatted = [] end reformatted << word end processed << reformatted end processed.reduce([]) { |a, e| a << e.join(' ') } end protected # @!attribute [r] text # @return [String] attr_reader :text # @!attribute [r] options # @return [Hash] attr_reader :options private # @param text_as_lines [Array] # @return [Vedeu::Views::Lines] def to_line_objects(text_as_lines) line_objects = Array(text_as_lines).map do |text_line| stream = Vedeu::Views::Stream.new(value: text_line) line = Vedeu::Views::Line.new stream.parent = line line.add(stream) line end Vedeu::Views::Lines.new(line_objects) end # Returns the text as an array of lines, split on '\n'. # # @return [Array] def split_lines text.split(/\n/) end # @param string [String] # @return [String] def ellipsis_string(string) return prune_string(string) if string.size < ellipsis.size "#{prune_string(string)}#{ellipsis}" end # Returns the string pruned. # # @param string [String] # @return [String] def prune_string(string) string.chomp.slice(0..pruned_width) end # Returns the width of the string minus the ellipsis. # # @return [Fixnum] def pruned_width width - ellipsis.size end # For when using mode `:prune`, by default, provides '...'. # # @return [String] def ellipsis options[:ellipsis] end # @return [Vedeu::Geometries::Geometry] def geometry Vedeu.geometry.by_name(name) end # Returns the word wrapping mode. One of :default, :prune or # :wrap; # # :default = Renders the content as is. # :prune = Discards the remainder of the content line # after width. # :wrap = Forces the content on to a new line after # width. # # @return [Symbol] def mode options[:mode] end # Returns the value of the :name option. When given, and a # geometry is registered with this name, the width of the # geometry is used if the :width option was not given. # # @return [String|Symbol] def name options[:name] end # Returns a boolean indicating the :name option was given and # a geometry is registered with that name. # # @return [Boolean] def registered? present?(name) && Vedeu.geometries.registered?(name) end # Returns the width to prune or wrap to. # # @raise [Vedeu::Error::MissingRequired] # @return [Fixnum] def width return options[:width] if present?(options[:width]) return geometry.width if registered? fail Vedeu::Error::MissingRequired, 'The text provided cannot be wrapped or pruned because a :width ' \ 'option was not given, or a :name option was either not given ' \ 'or there is no geometry registered with that name.' end # Returns the default options/attributes for this class. # # @return [Hash NilClass, String, Symbol>] def defaults { ellipsis: '...', mode: :default, name: nil, width: nil, } end end # Wordwrap end # DSL end # Vedeu