strings 0.1.4 → 0.2.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.
@@ -1,4 +1,4 @@
1
- # frozen_string_litera: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Strings
4
4
  # A class responsible for parsing padding value
@@ -45,7 +45,7 @@ module Strings
45
45
  elsif value.size == 4
46
46
  value
47
47
  else
48
- raise ParseError, 'Wrong :padding parameter, must be an array'
48
+ raise ParseError, "Wrong :padding parameter, must be an array"
49
49
  end
50
50
  end
51
51
 
@@ -72,7 +72,7 @@ module Strings
72
72
 
73
73
  # Set top padding
74
74
  #
75
- # @param [Integer] val
75
+ # @param [Integer] value
76
76
  #
77
77
  # @return [nil]
78
78
  #
@@ -92,7 +92,7 @@ module Strings
92
92
 
93
93
  # Set right padding
94
94
  #
95
- # @param [Integer] val
95
+ # @param [Integer] value
96
96
  #
97
97
  # @api public
98
98
  def right=(value)
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'strings-ansi'
4
- require 'unicode/display_width'
5
- require 'unicode_utils/each_grapheme'
3
+ require "strings-ansi"
4
+ require "unicode/display_width"
5
+ require "unicode_utils/each_grapheme"
6
6
 
7
7
  module Strings
8
8
  # A module responsible for text truncation
9
9
  module Truncate
10
- DEFAULT_TRAILING = ''.freeze
10
+ DEFAULT_TRAILING = "".freeze
11
11
 
12
12
  DEFAULT_LENGTH = 30
13
13
 
@@ -27,15 +27,15 @@ module Strings
27
27
  # text = "The sovereignest thing on earth is parmacetti for an inward bruise."
28
28
  #
29
29
  # Strings::Truncate.truncate(text)
30
- # # => "The sovereignest thing on ear…"
30
+ # # => "The sovereignest thing on ea…"
31
31
  #
32
32
  # Strings::Truncate.truncate(text, 20)
33
- # # => "The sovereignest th…"
33
+ # # => "The sovereignest t…"
34
34
  #
35
- # Strings::Truncate.truncate(text, 20, separator: ' ' )
35
+ # Strings::Truncate.truncate(text, 20, separator: " " )
36
36
  # # => "The sovereignest…"
37
37
  #
38
- # Strings::Truncate.truncate(40, trailing: '... (see more)' )
38
+ # Strings::Truncate.truncate(text, 40, trailing: "... (see more)" )
39
39
  # # => "The sovereignest thing on... (see more)"
40
40
  #
41
41
  # @api public
@@ -70,6 +70,7 @@ module Strings
70
70
  # @api private
71
71
  def shorten(original_chars, chars, length_without_trailing)
72
72
  truncated = []
73
+ return truncated if length_without_trailing.zero?
73
74
  char_width = display_width(chars[0])
74
75
  while length_without_trailing - char_width > 0
75
76
  orig_char = original_chars.shift
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Strings
4
- VERSION = '0.1.4'
4
+ VERSION = "0.2.0"
5
5
  end # Strings
@@ -1,55 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'strings-ansi'
4
- require 'unicode/display_width'
5
- require 'unicode_utils/each_grapheme'
6
-
7
- require_relative 'fold'
3
+ require "strings-ansi"
4
+ require "unicode/display_width"
5
+ require "unicode_utils/each_grapheme"
8
6
 
9
7
  module Strings
10
8
  module Wrap
11
9
  DEFAULT_WIDTH = 80
12
-
13
- NEWLINE = "\n".freeze
14
-
15
- SPACE = ' '.freeze
16
-
17
- LINE_BREAK = "\r\n+|\r+|\n+".freeze
10
+ NEWLINE = "\n"
11
+ SPACE = " "
12
+ LINE_BREAK = %r{\r\n|\r|\n}.freeze
13
+ LINE_BREAKS = "\r\n+|\r+|\n+"
18
14
 
19
15
  # Wrap a text into lines no longer than wrap_at length.
20
16
  # Preserves existing lines and existing word boundaries.
21
17
  #
22
18
  # @example
23
19
  # Strings::Wrap.wrap("Some longish text", 8)
24
- # # => >Some
25
- # >longish
26
- # >text
20
+ # # => "Some \nlongish \ntext"
27
21
  #
28
22
  # @api public
29
- def wrap(text, wrap_at = DEFAULT_WIDTH)
30
- if text.length < wrap_at.to_i || wrap_at.to_i.zero?
23
+ def wrap(text, wrap_at = DEFAULT_WIDTH, separator: NEWLINE)
24
+ if text.scan(/[[:print:]]/).length < wrap_at.to_i || wrap_at.to_i.zero?
31
25
  return text
32
26
  end
27
+
33
28
  ansi_stack = []
34
- text.split(%r{#{LINE_BREAK}}, -1).map do |paragraph|
35
- format_paragraph(paragraph, wrap_at, ansi_stack)
36
- end * NEWLINE
29
+ text.lines.map do |line|
30
+ format_line(line, wrap_at, ansi_stack).join(separator)
31
+ end.join
37
32
  end
38
33
  module_function :wrap
39
34
 
40
- # Format paragraph to be maximum of wrap_at length
35
+ # Format line to be maximum of wrap_at length
41
36
  #
42
- # @param [String] paragraph
43
- # the paragraph to format
37
+ # @param [String] text_line
38
+ # the line to format
44
39
  # @param [Integer] wrap_at
45
- # the maximum length to wrap the paragraph
40
+ # the maximum length to wrap the line
46
41
  #
47
42
  # @return [Array[String]]
48
43
  # the wrapped lines
49
44
  #
50
45
  # @api private
51
- def format_paragraph(paragraph, wrap_at, ansi_stack)
52
- cleared_para = Fold.fold(paragraph)
46
+ def format_line(text_line, wrap_at, ansi_stack)
53
47
  lines = []
54
48
  line = []
55
49
  word = []
@@ -58,10 +52,10 @@ module Strings
58
52
  word_length = 0
59
53
  line_length = 0
60
54
  char_length = 0 # visible char length
61
- text_length = display_width(cleared_para)
55
+ text_length = display_width(text_line)
62
56
  total_length = 0
63
57
 
64
- UnicodeUtils.each_grapheme(cleared_para) do |char|
58
+ UnicodeUtils.each_grapheme(text_line) do |char|
65
59
  # we found ansi let's consume
66
60
  if char == Strings::ANSI::CSI || ansi.length > 0
67
61
  ansi << char
@@ -70,7 +64,12 @@ module Strings
70
64
  elsif ansi_matched
71
65
  ansi_stack << [ansi[0...-1].join, line_length + word_length]
72
66
  ansi_matched = false
73
- ansi = []
67
+
68
+ if ansi.last == Strings::ANSI::CSI
69
+ ansi = [ansi.last]
70
+ else
71
+ ansi = []
72
+ end
74
73
  end
75
74
  next if ansi.length > 0
76
75
  end
@@ -91,65 +90,76 @@ module Strings
91
90
  end
92
91
 
93
92
  if char == SPACE # ends with space
94
- lines << insert_ansi(ansi_stack, line.join)
93
+ lines << insert_ansi(line.join, ansi_stack)
95
94
  line = []
96
95
  line_length = 0
97
96
  word << char
98
97
  word_length += char_length
99
98
  elsif word_length + char_length <= wrap_at
100
- lines << insert_ansi(ansi_stack, line.join)
99
+ lines << insert_ansi(line.join, ansi_stack)
101
100
  line = [word.join + char]
102
101
  line_length = word_length + char_length
103
102
  word = []
104
103
  word_length = 0
105
104
  else # hyphenate word - too long to fit a line
106
- lines << insert_ansi(ansi_stack, word.join)
105
+ lines << insert_ansi(word.join, ansi_stack)
107
106
  line_length = 0
108
107
  word = [char]
109
108
  word_length = char_length
110
109
  end
111
110
  end
112
- lines << insert_ansi(ansi_stack, line.join) unless line.empty?
113
- lines << insert_ansi(ansi_stack, word.join) unless word.empty?
111
+ lines << insert_ansi(line.join, ansi_stack) unless line.empty?
112
+ lines << insert_ansi(word.join, ansi_stack) unless word.empty?
114
113
  lines
115
114
  end
116
- module_function :format_paragraph
115
+ module_function :format_line
117
116
 
118
117
  # Insert ANSI code into string
119
118
  #
120
119
  # Check if there are any ANSI states, if present
121
120
  # insert ANSI codes at given positions unwinding the stack.
122
121
  #
123
- # @param [Array[Array[String, Integer]]] ansi_stack
124
- # the ANSI codes to apply
125
- #
126
122
  # @param [String] string
127
123
  # the string to insert ANSI codes into
128
124
  #
125
+ # @param [Array[Array[String, Integer]]] ansi_stack
126
+ # the ANSI codes to apply
127
+ #
129
128
  # @return [String]
130
129
  #
131
130
  # @api private
132
- def insert_ansi(ansi_stack, string)
131
+ def insert_ansi(string, ansi_stack = [])
133
132
  return string if ansi_stack.empty?
134
- to_remove = 0
135
- reset_index = -1
136
- output = string.dup
137
- resetting = false
138
- ansi_stack.reverse_each do |state|
139
- if state[0] =~ /#{Regexp.quote(Strings::ANSI::RESET)}/
140
- resetting = true
141
- reset_index = state[1]
142
- to_remove += 2
133
+ return string if string.empty?
134
+
135
+ new_stack = []
136
+ output = string.dup
137
+ length = string.size
138
+ matched_reset = false
139
+ ansi_reset = Strings::ANSI::RESET
140
+
141
+ # Reversed so that string index don't count ansi
142
+ ansi_stack.reverse_each do |ansi|
143
+ if ansi[0] =~ /#{Regexp.quote(ansi_reset)}/
144
+ matched_reset = true
145
+ output.insert(ansi[1], ansi_reset)
143
146
  next
144
- elsif !resetting
145
- reset_index = -1
146
- resetting = false
147
+ elsif !matched_reset # ansi without reset
148
+ matched_reset = false
149
+ new_stack << ansi # keep the ansi
150
+ next if ansi[1] == length
151
+ if output.end_with?(NEWLINE)
152
+ output.insert(-2, ansi_reset)
153
+ else
154
+ output.insert(-1, ansi_reset) # add reset at the end
155
+ end
147
156
  end
148
157
 
149
- color, color_index = *state
150
- output.insert(reset_index, Strings::ANSI::RESET).insert(color_index, color)
158
+ output.insert(ansi[1], ansi[0])
151
159
  end
152
- ansi_stack.pop(to_remove) # remove used states
160
+
161
+ ansi_stack.replace(new_stack)
162
+
153
163
  output
154
164
  end
155
165
  module_function :insert_ansi
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strings
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-10 00:00:00.000000000 Z
11
+ date: 2020-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: strings-ansi
@@ -16,98 +16,84 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.0
19
+ version: '0.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.1.0
26
+ version: '0.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: unicode_utils
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.4.0
33
+ version: '1.4'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.4.0
40
+ version: '1.4'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: unicode-display_width
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.4.0
47
+ version: '1.5'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.4.0
55
- - !ruby/object:Gem::Dependency
56
- name: bundler
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1.15'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.15'
54
+ version: '1.5'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rake
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
- - - "~>"
59
+ - - ">="
74
60
  - !ruby/object:Gem::Version
75
- version: '10.0'
61
+ version: '0'
76
62
  type: :development
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
- - - "~>"
66
+ - - ">="
81
67
  - !ruby/object:Gem::Version
82
- version: '10.0'
68
+ version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: rspec
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
- - - "~>"
73
+ - - ">="
88
74
  - !ruby/object:Gem::Version
89
75
  version: '3.0'
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
- - - "~>"
80
+ - - ">="
95
81
  - !ruby/object:Gem::Version
96
82
  version: '3.0'
97
- description: A set of useful functions such as fold, truncate, wrap and more for transoforming
98
- strings.
83
+ description: A set of methods for working with strings such as align, truncate, wrap
84
+ and many more.
99
85
  email:
100
- - ''
86
+ - piotr@piotrmurach.com
101
87
  executables: []
102
88
  extensions: []
103
- extra_rdoc_files: []
89
+ extra_rdoc_files:
90
+ - README.md
91
+ - CHANGELOG.md
92
+ - LICENSE.txt
104
93
  files:
105
94
  - CHANGELOG.md
106
95
  - LICENSE.txt
107
96
  - README.md
108
- - Rakefile
109
- - bin/console
110
- - bin/setup
111
97
  - lib/strings.rb
112
98
  - lib/strings/align.rb
113
99
  - lib/strings/extensions.rb
@@ -117,31 +103,15 @@ files:
117
103
  - lib/strings/truncate.rb
118
104
  - lib/strings/version.rb
119
105
  - lib/strings/wrap.rb
120
- - spec/spec_helper.rb
121
- - spec/unit/align/align_left_spec.rb
122
- - spec/unit/align/align_right_spec.rb
123
- - spec/unit/align/align_spec.rb
124
- - spec/unit/align_spec.rb
125
- - spec/unit/ansi_spec.rb
126
- - spec/unit/extensions_spec.rb
127
- - spec/unit/fold/fold_spec.rb
128
- - spec/unit/fold_spec.rb
129
- - spec/unit/pad/pad_spec.rb
130
- - spec/unit/pad_spec.rb
131
- - spec/unit/padder/parse_spec.rb
132
- - spec/unit/sanitize_spec.rb
133
- - spec/unit/truncate/truncate_spec.rb
134
- - spec/unit/truncate_spec.rb
135
- - spec/unit/wrap/wrap_spec.rb
136
- - spec/unit/wrap_spec.rb
137
- - strings.gemspec
138
- - tasks/console.rake
139
- - tasks/coverage.rake
140
- - tasks/spec.rake
141
- homepage: ''
106
+ homepage: https://github.com/piotrmurach/strings
142
107
  licenses:
143
108
  - MIT
144
- metadata: {}
109
+ metadata:
110
+ allowed_push_host: https://rubygems.org
111
+ changelog_uri: https://github.com/piotrmurach/strings/blob/master/CHANGELOG.md
112
+ documentation_uri: https://www.rubydoc.info/gems/strings
113
+ homepage_uri: https://github.com/piotrmurach/strings
114
+ source_code_uri: https://github.com/piotrmurach/strings
145
115
  post_install_message:
146
116
  rdoc_options: []
147
117
  require_paths:
@@ -150,16 +120,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
150
120
  requirements:
151
121
  - - ">="
152
122
  - !ruby/object:Gem::Version
153
- version: '0'
123
+ version: 2.0.0
154
124
  required_rubygems_version: !ruby/object:Gem::Requirement
155
125
  requirements:
156
126
  - - ">="
157
127
  - !ruby/object:Gem::Version
158
128
  version: '0'
159
129
  requirements: []
160
- rubyforge_project:
161
- rubygems_version: 2.7.3
130
+ rubygems_version: 3.1.2
162
131
  signing_key:
163
132
  specification_version: 4
164
- summary: A set of useful functions for transforming strings.
133
+ summary: A set of methods for working with strings.
165
134
  test_files: []