strings 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ ---
2
+ install:
3
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
4
+ - ruby --version
5
+ - gem --version
6
+ - bundle install
7
+ build: off
8
+ test_script:
9
+ - bundle exec rake ci
10
+ environment:
11
+ matrix:
12
+ - ruby_version: "200"
13
+ - ruby_version: "200-x64"
14
+ - ruby_version: "21"
15
+ - ruby_version: "21-x64"
16
+ - ruby_version: "22"
17
+ - ruby_version: "22-x64"
18
+ - ruby_version: "23"
19
+ - ruby_version: "23-x64"
20
+ - ruby_version: "24"
21
+ - ruby_version: "24-x64"
Binary file
@@ -0,0 +1,30 @@
1
+ require 'benchmark/ips'
2
+ require 'strings'
3
+
4
+ text = "Ignorance is the parent of fear."
5
+
6
+ Benchmark.ips do |x|
7
+ x.report('wrap') do
8
+ Strings::Wrap.wrap(text, 10)
9
+ end
10
+
11
+ x.report('truncate') do
12
+ Strings::Truncate.truncate(text, 10)
13
+ end
14
+
15
+ x.compare!
16
+ end
17
+
18
+ # (2017-12-09)
19
+ #
20
+ # Calculating -------------------------------------
21
+ # wrap 186 i/100ms
22
+ # truncate 295 i/100ms
23
+ # -------------------------------------------------
24
+ # wrap 1917.2 (±2.2%) i/s - 9672 in 5.047297s
25
+ # truncate 3020.6 (±3.0%) i/s - 15340 in 5.083516s
26
+ #
27
+ # Comparison:
28
+ # truncate: 3020.6 i/s
29
+ # wrap: 1917.2 i/s - 1.58x slower
30
+ #
@@ -1,5 +1,111 @@
1
- require "strings/version"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'strings/align'
4
+ require_relative 'strings/ansi'
5
+ require_relative 'strings/fold'
6
+ require_relative 'strings/pad'
7
+ require_relative 'strings/truncate'
8
+ require_relative 'strings/wrap'
9
+ require_relative 'strings/version'
2
10
 
3
11
  module Strings
4
- # Your code goes here...
5
- end
12
+ # Align text within the width.
13
+ #
14
+ # @see Strings::Align#align
15
+ #
16
+ # @api public
17
+ def align(*args)
18
+ Align.align(*args)
19
+ end
20
+ module_function :align
21
+
22
+ # Align text left within the width.
23
+ #
24
+ # @see Strings::Align#align_left
25
+ #
26
+ # @api public
27
+ def align_left(*args)
28
+ Align.align_left(*args)
29
+ end
30
+ module_function :align_left
31
+
32
+ # Align text with the width.
33
+ #
34
+ # @see Strings::Align#align
35
+ #
36
+ # @api public
37
+ def align_center(*args)
38
+ Align.align_center(*args)
39
+ end
40
+ module_function :align_center
41
+
42
+ # Align text with the width.
43
+ #
44
+ # @see Strings::Align#align
45
+ #
46
+ # @api public
47
+ def align_right(*args)
48
+ Align.align_right(*args)
49
+ end
50
+ module_function :align_right
51
+
52
+ # Check if string contains ANSI codes
53
+ #
54
+ # @see Strings::ANSI#ansi?
55
+ #
56
+ # @api public
57
+ def ansi?(string)
58
+ ANSI.ansi?(string)
59
+ end
60
+ module_function :ansi?
61
+
62
+ # Remove any line break characters from the text
63
+ #
64
+ # @see Strings::Fold#fold
65
+ #
66
+ # @api public
67
+ def fold(*args)
68
+ Fold.fold(*args)
69
+ end
70
+ module_function :fold
71
+
72
+ # Apply padding to multiline text with ANSI codes
73
+ #
74
+ # @see Strings::Pad#pad
75
+ #
76
+ # @api public
77
+ def pad(*args)
78
+ Pad.pad(*args)
79
+ end
80
+ module_function :pad
81
+
82
+ # Remove ANSI codes from the string
83
+ #
84
+ # @see Strings::ANSI#sanitize
85
+ #
86
+ # @api public
87
+ def sanitize(text)
88
+ ANSI.sanitize(text)
89
+ end
90
+ module_function :sanitize
91
+
92
+ # Truncate a text at a given length
93
+ #
94
+ # @see Strings::Truncate#truncate
95
+ #
96
+ # @api public
97
+ def truncate(text, truncate_at, options = {})
98
+ Truncate.truncate(text, truncate_at, options)
99
+ end
100
+ module_function :truncate
101
+
102
+ # Wrap a text into lines at wrap length
103
+ #
104
+ # @see Strings::Wrap#wrap
105
+ #
106
+ # @api public
107
+ def wrap(text, wrap_at)
108
+ Wrap.wrap(text, wrap_at)
109
+ end
110
+ module_function :wrap
111
+ end # Strings
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'unicode/display_width'
4
+
5
+ require_relative 'ansi'
6
+
7
+ module Strings
8
+ # Responsible for text alignment
9
+ module Align
10
+ NEWLINE = "\n".freeze
11
+
12
+ SPACE = ' '.freeze
13
+
14
+ # Aligns text within the width.
15
+ #
16
+ # If the text is greater than the width then unmodified
17
+ # string is returned.
18
+ #
19
+ # @param [String] text
20
+ # the text to align lines of
21
+ # @param [Integer] width
22
+ # the maximum width to align to
23
+ #
24
+ # @example
25
+ # text = "the madness of men"
26
+ #
27
+ # Strings::Align.align(22, :left)
28
+ # # => "the madness of men "
29
+ #
30
+ # Strings::Align.align(22, :center)
31
+ # # => " the madness of men "
32
+ #
33
+ # Strings::Align(22, :right)
34
+ # # => " the madness of men"
35
+ #
36
+ # Strings::Align.align(22, :center, fill: '*)
37
+ # # => "***the madness of men***"
38
+ #
39
+ # @api public
40
+ def align(text, width, direction: :left, **options)
41
+ return text if width.nil?
42
+ method = to_alignment(direction)
43
+ send(method, text, width, options)
44
+ end
45
+ module_function :align
46
+
47
+ # Convert direction to method name
48
+ #
49
+ # @api private
50
+ def to_alignment(direction)
51
+ case direction.to_sym
52
+ when :left then :align_left
53
+ when :right then :align_right
54
+ when :center then :align_center
55
+ else
56
+ raise ArgumentError, "Unknown alignment `#{direction}`."
57
+ end
58
+ end
59
+ module_function :to_alignment
60
+
61
+ # Aligns text to the left at given length
62
+ #
63
+ # @return [String]
64
+ #
65
+ # @api public
66
+ def align_left(text, width, fill: SPACE, separator: NEWLINE)
67
+ return if width.nil?
68
+ each_line(text, separator) do |line|
69
+ width_diff = width - display_width(line)
70
+ if width_diff > 0
71
+ line + fill * width_diff
72
+ else
73
+ line
74
+ end
75
+ end
76
+ end
77
+ module_function :align_left
78
+
79
+ # Centers text within the width
80
+ #
81
+ # @return [String]
82
+ #
83
+ # @api public
84
+ def align_center(text, width, fill: SPACE, separator: NEWLINE)
85
+ return text if width.nil?
86
+ each_line(text, separator) do |line|
87
+ width_diff = width - display_width(line)
88
+ if width_diff > 0
89
+ right_count = (width_diff.to_f / 2).ceil
90
+ left_count = width_diff - right_count
91
+ [fill * left_count, line, fill * right_count].join
92
+ else
93
+ text
94
+ end
95
+ end
96
+ end
97
+ module_function :align_center
98
+
99
+ # Aligns text to the right at given length
100
+ #
101
+ # @return [String]
102
+ #
103
+ # @api public
104
+ def align_right(text, width, fill: SPACE, separator: NEWLINE)
105
+ return text if width.nil?
106
+ each_line(text, separator) do |line|
107
+ width_diff = width - display_width(line)
108
+ if width_diff > 0
109
+ fill * width_diff + line
110
+ else
111
+ line
112
+ end
113
+ end
114
+ end
115
+ module_function :align_right
116
+
117
+ # Enumerate text line by line
118
+ #
119
+ # @param [String] text
120
+ #
121
+ # @return [String]
122
+ #
123
+ # @api private
124
+ def each_line(text, separator)
125
+ lines = text.split(separator)
126
+ return yield(text) if text.empty?
127
+ lines.reduce([]) do |aligned, line|
128
+ aligned << yield(line)
129
+ end.join(separator)
130
+ end
131
+ module_function :each_line
132
+
133
+ # Visible width of a string
134
+ #
135
+ # @api private
136
+ def display_width(string)
137
+ Unicode::DisplayWidth.of(Strings::ANSI.sanitize(string))
138
+ end
139
+ module_function :display_width
140
+ end # Align
141
+ end # Strings
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strings
4
+ # Helper functions for handling ANSI escape sequences
5
+ module ANSI
6
+ # The control sequence indicator
7
+ CSI = "\033".freeze
8
+
9
+ # The code for reseting styling
10
+ RESET = "\e[0m".freeze
11
+
12
+ # The regex to match ANSI codes
13
+ ANSI_MATCHER = '(\[)?\033(\[)?[;?\d]*[\dA-Za-z]([\];])?'.freeze
14
+
15
+ # Remove ANSI characters from the text
16
+ #
17
+ # @param [String] text
18
+ #
19
+ # @example
20
+ # Strings::ANSI.sanitize("\e[33mfoo\[e0m")
21
+ # # => "foo"
22
+ #
23
+ # @return [String]
24
+ #
25
+ # @api public
26
+ def sanitize(text)
27
+ text.gsub(/#{ANSI_MATCHER}/, '')
28
+ end
29
+ module_function :sanitize
30
+
31
+ # Check if string contains ANSI codes
32
+ #
33
+ # @param [String] string
34
+ # the string to check
35
+ #
36
+ # @example
37
+ # Strings::ANSI.ansi?("\e[33mfoo\[e0m")
38
+ # # => true
39
+ #
40
+ # @return [Boolean]
41
+ #
42
+ # @api public
43
+ def ansi?(string)
44
+ !!(string =~ /#{ANSI_MATCHER}/)
45
+ end
46
+ module_function :ansi?
47
+
48
+ # Check if string contains only ANSI codes
49
+ #
50
+ # @param [String] string
51
+ # the string to check
52
+ #
53
+ # @example
54
+ # Strings::ANSI.only_ansi?("\e[33mfoo\[e0m")
55
+ # # => false
56
+ #
57
+ # Strings::ANSI.only_ansi?("\e[33m")
58
+ # # => false
59
+ #
60
+ # @return [Boolean]
61
+ #
62
+ # @api public
63
+ def only_ansi?(string)
64
+ !!(string =~ /^#{ANSI_MATCHER}$/)
65
+ end
66
+ module_function :only_ansi?
67
+ end # Sanitizer
68
+ end # Strings
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strings
4
+ module Fold
5
+ LINE_BREAK = "(\r\n+|\r+|\n+|\t+)".freeze
6
+
7
+ # Fold a multiline text into a single line string
8
+ #
9
+ # @example
10
+ # fold("\tfoo \r\n\n bar") # => "foo bar"
11
+ #
12
+ # @param [String] text
13
+ #
14
+ # @param [String] separator
15
+ # the separators to be removed from the text, default: (\r\n+|\r+|\n+|\t+)
16
+ #
17
+ # @return [String]
18
+ #
19
+ # @api public
20
+ def fold(text, separator = LINE_BREAK)
21
+ text.gsub(/([ ]+)#{separator}/, "\\1")
22
+ .gsub(/#{separator}(?<space>[ ]+)/, "\\k<space>")
23
+ .gsub(/#{separator}/, ' ')
24
+ end
25
+ module_function :fold
26
+ end # Fold
27
+ end # Strings
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'unicode/display_width'
4
+
5
+ require_relative 'ansi'
6
+ require_relative 'padder'
7
+
8
+ module Strings
9
+ # Responsible for text padding
10
+ module Pad
11
+ NEWLINE = "\n".freeze
12
+
13
+ SPACE = ' '.freeze
14
+
15
+ # Apply padding to multiline text with ANSI codes
16
+ #
17
+ # @param [String] text
18
+ # the text to pad out
19
+ # @param [Integer, Array[Integer]] padding
20
+ # the padding to apply to text
21
+ #
22
+ # @example
23
+ # text = "Ignorance is the parent of fear."
24
+ #
25
+ # Strings::Pad.pad(text, [1, 2], fill: "*")
26
+ # # =>
27
+ # # "************************************\n"
28
+ # # "**Ignorance is the parent of fear.**\n"
29
+ # # "************************************\n"
30
+ #
31
+ # @return [String]
32
+ #
33
+ # @api private
34
+ def pad(text, padding, fill: SPACE, separator: NEWLINE)
35
+ padding = Strings::Padder.parse(padding)
36
+ text_copy = text.dup
37
+ line_width = max_line_length(text, separator)
38
+ output = []
39
+
40
+ filler_line = fill * line_width
41
+
42
+ padding.top.times do
43
+ output << pad_around(filler_line, padding, fill: fill)
44
+ end
45
+
46
+ text_copy.split(separator).each do |line|
47
+ output << pad_around(line, padding, fill: fill)
48
+ end
49
+
50
+ padding.bottom.times do
51
+ output << pad_around(filler_line, padding, fill: fill)
52
+ end
53
+
54
+ output.join(separator)
55
+ end
56
+ module_function :pad
57
+
58
+ # Apply padding to left and right side of string
59
+ #
60
+ # @param [String] text
61
+ #
62
+ # @return [String]
63
+ #
64
+ # @api private
65
+ def pad_around(text, padding, fill: SPACE)
66
+ fill * padding.left + text + fill * padding.right
67
+ end
68
+ module_function :pad_around
69
+
70
+ # Determine maximum length for all multiline content
71
+ #
72
+ # @param [String] text
73
+ # @param [String] separator
74
+ #
75
+ # @return [Integer]
76
+ #
77
+ # @api private
78
+ def max_line_length(text, separator)
79
+ lines = text.split(separator, -1)
80
+ display_width(lines.max_by { |line| display_width(line) } || '')
81
+ end
82
+ module_function :max_line_length
83
+
84
+ # Calculate visible string width
85
+ #
86
+ # @return [Integer]
87
+ #
88
+ # @api private
89
+ def display_width(string)
90
+ Unicode::DisplayWidth.of(Strings::ANSI.sanitize(string))
91
+ end
92
+ module_function :display_width
93
+ end # Padding
94
+ end # Strings