visual_width 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/README.md +22 -2
- data/example/table.rb +13 -0
- data/lib/visual_width/string_ext.rb +1 -1
- data/lib/visual_width/string_refine.rb +1 -1
- data/lib/visual_width/table.rb +83 -0
- data/lib/visual_width/version.rb +1 -1
- data/lib/visual_width.rb +19 -19
- data/spec/visual_width/string_refine_spec.rb +4 -1
- data/spec/visual_width/table_spec.rb +77 -0
- data/spec/visual_width_spec.rb +25 -23
- data/tool/benchmark.rb +3 -3
- data/tool/tables.rb +21 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4de483063c67cddbaca1f3ac3a1ac27536311218
|
4
|
+
data.tar.gz: 3d9ab9b361e41e1edc9d2f095c28a3591e72cfbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd041e918922cf48e5686b8d9d7c4417b8bbde49c11fa65f6f2d3c6783f0afccd6f41397d2aae6b75f94ed3cdba3e3fbdd04901433df06ee1ee935ed724b68e0
|
7
|
+
data.tar.gz: 40a336d2d2e0e2b919109744910a17cb2860dfe13e59c69c7766c3707f65a614e8226402c1bbf5711953f81a154f1022c3c7e8df7bc93b76755d54fcd429f815
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -21,12 +21,28 @@ Or install it yourself as:
|
|
21
21
|
## Usage
|
22
22
|
|
23
23
|
```ruby
|
24
|
+
# VisualWidth
|
24
25
|
require 'visual_width'
|
25
26
|
|
26
|
-
p VisualWidth.
|
27
|
-
p VisualWidth.
|
27
|
+
p VisualWidth.measure("こんにちは") # => 10
|
28
|
+
p VisualWidth.measure("abcdefghij") # => 10
|
28
29
|
|
29
30
|
p VisualWidth.truncate("恋すてふ 我が名はまだき 立ちにけり 人知れずこそ 思ひそめしか", 20) # => "恋すてふ 我が名は..."
|
31
|
+
|
32
|
+
# VisualWidth::Table
|
33
|
+
require 'visual_width/table'
|
34
|
+
|
35
|
+
t = VisualWidth::Table.new(
|
36
|
+
format: [VisualWidth::Table::LEFT, VisualWidth::Table::RIGHT, VisualWidth::Table::RIGHT]
|
37
|
+
)
|
38
|
+
|
39
|
+
header = ['Student', 'Mid-Terms', 'Finals']
|
40
|
+
rows = [
|
41
|
+
['Sam', 94, 93],
|
42
|
+
['Jane', 92, 99],
|
43
|
+
['Average', 93, 96],
|
44
|
+
]
|
45
|
+
t.draw(rows, header: header)
|
30
46
|
```
|
31
47
|
|
32
48
|
Each method can take `east_asian: false` to tell it is not in an East Asian context, regarding ambiguous characters as half-width.
|
@@ -40,3 +56,7 @@ See [Ambiguous Characters](http://www.unicode.org/reports/tr11/#Ambiguous) in th
|
|
40
56
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
41
57
|
4. Push to the branch (`git push origin my-new-feature`)
|
42
58
|
5. Create new Pull Request
|
59
|
+
|
60
|
+
## See Also
|
61
|
+
|
62
|
+
* [unicode-display_width](https://rubygems.org/gems/unicode-display_width) has the same feature as `VisualWidth.measure()` but it extends String class directly and is much slower than `VisualWidth.measure()`
|
data/example/table.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# VisualWidth::Table
|
2
|
+
require 'visual_width/table'
|
3
|
+
t = VisualWidth::Table.new(
|
4
|
+
format: [VisualWidth::Table::LEFT, VisualWidth::Table::RIGHT, VisualWidth::Table::RIGHT]
|
5
|
+
)
|
6
|
+
|
7
|
+
header = ['Student', 'Mid-Terms', 'Finals']
|
8
|
+
rows = [
|
9
|
+
['Sam', 94, 93],
|
10
|
+
['Jane', 92, 99],
|
11
|
+
['Average', 93, 96],
|
12
|
+
]
|
13
|
+
puts t.draw(rows, header: header)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'visual_width'
|
2
|
+
|
3
|
+
class VisualWidth::Table
|
4
|
+
attr_accessor :header, :footer, :format
|
5
|
+
|
6
|
+
# align (left, right, center)
|
7
|
+
LEFT = -> (cell, fill) {
|
8
|
+
cell + (' ' * fill)
|
9
|
+
}
|
10
|
+
|
11
|
+
RIGHT = -> (cell, fill) {
|
12
|
+
(' ' * fill) + cell
|
13
|
+
}
|
14
|
+
|
15
|
+
CENTER = -> (cell, fill) {
|
16
|
+
half = fill / 2.0
|
17
|
+
(' ' * half.floor) + cell + (' ' * half.ceil)
|
18
|
+
}
|
19
|
+
|
20
|
+
def initialize(header: nil, format: [])
|
21
|
+
@header = header
|
22
|
+
@format = format
|
23
|
+
end
|
24
|
+
|
25
|
+
def draw(rows, header: nil, output: "")
|
26
|
+
max_widths = calc_max_widths(rows)
|
27
|
+
h = header || @header
|
28
|
+
if h
|
29
|
+
max_widths = h.map { |cell| VisualWidth.measure(cell) }
|
30
|
+
.zip(max_widths)
|
31
|
+
.map { |values| values.max }
|
32
|
+
format_header = [CENTER] * h.length
|
33
|
+
else
|
34
|
+
format_header = nil
|
35
|
+
end
|
36
|
+
draw_row(output, max_widths, format_header, h, separated: true)
|
37
|
+
rows.each do |row|
|
38
|
+
draw_row(output, max_widths, @format, row)
|
39
|
+
end
|
40
|
+
line(output, max_widths)
|
41
|
+
output
|
42
|
+
end
|
43
|
+
private
|
44
|
+
|
45
|
+
def draw_row(output, max_widths, format, row, separated: false)
|
46
|
+
if separated
|
47
|
+
line(output, max_widths)
|
48
|
+
end
|
49
|
+
|
50
|
+
if row
|
51
|
+
output << '|'
|
52
|
+
row.each_with_index do |cell, i|
|
53
|
+
fill(output, max_widths[i], cell.to_s, format[i])
|
54
|
+
output << '|'
|
55
|
+
end
|
56
|
+
output << "\n"
|
57
|
+
|
58
|
+
if separated
|
59
|
+
line(output, max_widths)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def line (output, max_widths)
|
65
|
+
output << '+' << max_widths.map { |width| '-' * width }.join('+') << "+\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
def fill(output, max_width, cell, f)
|
69
|
+
f ||= LEFT
|
70
|
+
w = VisualWidth.measure(cell)
|
71
|
+
output << f.call(cell, (max_width - w))
|
72
|
+
end
|
73
|
+
|
74
|
+
def calc_max_widths(rows) # -> [max_col0_width, max_col1_width, ...]
|
75
|
+
result = []
|
76
|
+
rows.each_with_index do |row|
|
77
|
+
row.each_with_index do |cell, i|
|
78
|
+
result[i] = [result[i] || 0, VisualWidth.measure(cell.to_s)].max
|
79
|
+
end
|
80
|
+
end
|
81
|
+
result
|
82
|
+
end
|
83
|
+
end
|
data/lib/visual_width/version.rb
CHANGED
data/lib/visual_width.rb
CHANGED
@@ -4,44 +4,44 @@ require "visual_width/data"
|
|
4
4
|
module VisualWidth
|
5
5
|
EAST_ASIAN = true
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
r1 = /#{Fullwide} | #{Wide} | #{Ambiguous}/x;
|
8
|
+
r0 = /#{Fullwide} | #{Wide} /x;
|
9
9
|
|
10
|
-
@@
|
11
|
-
@@
|
10
|
+
@@c1 = /( #{r1}+ )/x
|
11
|
+
@@c0 = /( #{r0}+ )/x
|
12
|
+
|
13
|
+
@@t1 = /( #{r1} ) | . /x
|
14
|
+
@@t0 = /( #{r0} ) | . /x
|
12
15
|
|
13
16
|
module_function
|
14
17
|
|
15
|
-
def count(
|
18
|
+
def count(*args)
|
19
|
+
warn "count() is deprecated. use measure() instead"
|
20
|
+
measure(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def measure(str, east_asian: EAST_ASIAN)
|
16
24
|
rx = east_asian ? @@c1 : @@c0
|
17
25
|
full_width = 0
|
18
|
-
str.scan(rx) do |
|
19
|
-
full_width +=
|
26
|
+
str.scan(rx) do |wide,|
|
27
|
+
full_width += wide.length
|
20
28
|
end
|
21
29
|
(full_width * 2) + (str.length - full_width)
|
22
30
|
end
|
23
31
|
|
24
32
|
def truncate(str, max_length, omission: '...', east_asian: EAST_ASIAN)
|
25
|
-
max = max_length - omission.length
|
26
33
|
rx = east_asian ? @@t1 : @@t0
|
34
|
+
max = max_length - omission.length
|
27
35
|
pos = 0
|
28
36
|
width = 0
|
29
37
|
str.scan(rx) do |wide,|
|
30
|
-
|
31
|
-
width += 2
|
32
|
-
else
|
33
|
-
width += 1
|
34
|
-
end
|
38
|
+
width += wide ? 2 : 1
|
35
39
|
|
36
|
-
if width > max
|
37
|
-
break
|
38
|
-
end
|
40
|
+
break if width > max
|
39
41
|
|
40
42
|
pos += 1
|
41
43
|
|
42
|
-
if width == max
|
43
|
-
break
|
44
|
-
end
|
44
|
+
break if width == max
|
45
45
|
end
|
46
46
|
|
47
47
|
if width < str.length
|
@@ -1,10 +1,13 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'rspec'
|
2
4
|
|
5
|
+
require 'visual_width/string_ext' # workaround Ruby 2.0.0 bug
|
3
6
|
require 'visual_width/string_refine'
|
4
7
|
|
5
8
|
using VisualWidth
|
6
9
|
|
7
|
-
describe VisualWidth do
|
10
|
+
describe VisualWidth, skip: RUBY_VERSION < "2.0.0" do
|
8
11
|
context "String#width" do
|
9
12
|
it do
|
10
13
|
expect("こんにちは".width).to eql(10)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
|
5
|
+
require 'visual_width/table'
|
6
|
+
|
7
|
+
describe VisualWidth::Table do
|
8
|
+
let (:rows) do
|
9
|
+
rows = []
|
10
|
+
rows << %w(りんご なし)
|
11
|
+
rows << %w(べにしぐれ せいぎょく)
|
12
|
+
rows << %w(となみ いなぎ)
|
13
|
+
rows
|
14
|
+
end
|
15
|
+
|
16
|
+
context "#draw" do
|
17
|
+
it 'draws text table' do
|
18
|
+
t = VisualWidth::Table.new
|
19
|
+
|
20
|
+
expect(t.draw(rows)).to eql(<<-'TEXT')
|
21
|
+
+----------+----------+
|
22
|
+
|りんご |なし |
|
23
|
+
|べにしぐれ|せいぎょく|
|
24
|
+
|となみ |いなぎ |
|
25
|
+
+----------+----------+
|
26
|
+
TEXT
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'draws text table with formatter/CENTER, RIGHT' do
|
30
|
+
t = VisualWidth::Table.new(
|
31
|
+
format: [VisualWidth::Table::CENTER, VisualWidth::Table::RIGHT],
|
32
|
+
)
|
33
|
+
|
34
|
+
expect(t.draw(rows)).to eql(<<-'TEXT')
|
35
|
+
+----------+----------+
|
36
|
+
| りんご | なし|
|
37
|
+
|べにしぐれ|せいぎょく|
|
38
|
+
| となみ | いなぎ|
|
39
|
+
+----------+----------+
|
40
|
+
TEXT
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'draws text table with header/footer' do
|
44
|
+
t = VisualWidth::Table.new(
|
45
|
+
header: %w(A B),
|
46
|
+
)
|
47
|
+
expect(t.draw(rows)).to eql(<<-'TEXT')
|
48
|
+
+----------+----------+
|
49
|
+
| A | B |
|
50
|
+
+----------+----------+
|
51
|
+
|りんご |なし |
|
52
|
+
|べにしぐれ|せいぎょく|
|
53
|
+
|となみ |いなぎ |
|
54
|
+
+----------+----------+
|
55
|
+
TEXT
|
56
|
+
end
|
57
|
+
|
58
|
+
it "does jobs with non-string elements" do
|
59
|
+
header = ['Student', 'Mid-Terms', 'Finals']
|
60
|
+
rows = [
|
61
|
+
['Sam', 94, 93],
|
62
|
+
['Jane', 92, 99],
|
63
|
+
['Average', 93, 96],
|
64
|
+
]
|
65
|
+
|
66
|
+
expect(VisualWidth::Table.new.draw(rows, header: header)).to eql(<<-'TEXT')
|
67
|
+
+-------+---------+------+
|
68
|
+
|Student|Mid-Terms|Finals|
|
69
|
+
+-------+---------+------+
|
70
|
+
|Sam |94 |93 |
|
71
|
+
|Jane |92 |99 |
|
72
|
+
|Average|93 |96 |
|
73
|
+
+-------+---------+------+
|
74
|
+
TEXT
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/spec/visual_width_spec.rb
CHANGED
@@ -1,40 +1,42 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'rspec'
|
2
4
|
|
3
5
|
require 'visual_width'
|
4
6
|
require 'visual_width/string_ext'
|
5
7
|
|
6
8
|
describe VisualWidth do
|
7
|
-
context ".
|
8
|
-
it "
|
9
|
-
expect(VisualWidth.
|
9
|
+
context ".measure" do
|
10
|
+
it "measures Half characters" do
|
11
|
+
expect(VisualWidth.measure("foo")).to eql(3)
|
10
12
|
end
|
11
|
-
it "
|
12
|
-
expect(VisualWidth.
|
13
|
+
it "measures Wide characters" do
|
14
|
+
expect(VisualWidth.measure("こんにちは")).to eql(10)
|
13
15
|
end
|
14
16
|
|
15
|
-
it "
|
16
|
-
expect(VisualWidth.
|
17
|
+
it "measures Half characters" do
|
18
|
+
expect(VisualWidth.measure("コンニチハ")).to eql(5)
|
17
19
|
end
|
18
20
|
|
19
|
-
it "
|
20
|
-
expect(VisualWidth.
|
21
|
+
it "measures Ambiguous characters" do
|
22
|
+
expect(VisualWidth.measure("αβ")).to eql(4)
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
26
|
context ".counnt with east_asian: false" do
|
25
|
-
it "
|
26
|
-
expect(VisualWidth.
|
27
|
+
it "measures Halfwide characters" do
|
28
|
+
expect(VisualWidth.measure("foo", east_asian: false)).to eql(3)
|
27
29
|
end
|
28
|
-
it "
|
29
|
-
expect(VisualWidth.
|
30
|
+
it "measures Wide characters" do
|
31
|
+
expect(VisualWidth.measure("こんにちは", east_asian: false)).to eql(10)
|
30
32
|
end
|
31
33
|
|
32
|
-
it "
|
33
|
-
expect(VisualWidth.
|
34
|
+
it "measures Half characters" do
|
35
|
+
expect(VisualWidth.measure("コンニチハ", east_asian: false)).to eql(5)
|
34
36
|
end
|
35
37
|
|
36
|
-
it "
|
37
|
-
expect(VisualWidth.
|
38
|
+
it "measures Ambiguous characters" do
|
39
|
+
expect(VisualWidth.measure("αβ", east_asian: false)).to eql(2)
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
@@ -46,35 +48,35 @@ describe VisualWidth do
|
|
46
48
|
it "truncates str, adding '...'" do
|
47
49
|
str = "The quick brown fox jumps over the lazy dog."
|
48
50
|
s = VisualWidth.truncate(str, 15)
|
49
|
-
expect(s.
|
51
|
+
expect(s.width).to be <= 15
|
50
52
|
expect(s).to eql('The quick br...')
|
51
53
|
end
|
52
54
|
|
53
55
|
it "truncates str, adding '(snip)'" do
|
54
56
|
str = "The quick brown fox jumps over the lazy dog."
|
55
57
|
s = VisualWidth.truncate(str, 15, omission: '(snip)')
|
56
|
-
expect(s.
|
58
|
+
expect(s.width).to be <= 15
|
57
59
|
expect(s).to eql('The quick(snip)')
|
58
60
|
end
|
59
61
|
|
60
62
|
it "truncates Wide characters" do
|
61
63
|
str = "くにざかいのながいトンネルを抜けるとゆきぐにであった"
|
62
64
|
s = VisualWidth.truncate(str, 20)
|
63
|
-
expect(s.
|
65
|
+
expect(s.width).to be <= 20
|
64
66
|
expect(s).to eql('くにざかいのなが...')
|
65
67
|
end
|
66
68
|
|
67
69
|
it "truncates mixed character types" do
|
68
70
|
str = "くにざかいの ながい トンネルを 抜けると ゆきぐにであった"
|
69
71
|
s = VisualWidth.truncate(str, 20)
|
70
|
-
expect(s.
|
72
|
+
expect(s.width).to be <= 20
|
71
73
|
expect(s).to eql('くにざかいの なが...')
|
72
74
|
end
|
73
75
|
|
74
76
|
it "truncates Ambiguous characters" do
|
75
77
|
str = "αβγδεζηθικλμνξοπρστυφχψω"
|
76
78
|
s = VisualWidth.truncate(str, 10)
|
77
|
-
expect(s.
|
79
|
+
expect(s.width).to be <= 10
|
78
80
|
expect(s).to eql('αβγ...')
|
79
81
|
end
|
80
82
|
end
|
@@ -83,7 +85,7 @@ describe VisualWidth do
|
|
83
85
|
it "truncates str with Ambiguous characters" do
|
84
86
|
str = "αβγδεζηθικλμνξοπρστυφχψω"
|
85
87
|
s = VisualWidth.truncate(str, 10, east_asian: false)
|
86
|
-
expect(s.
|
88
|
+
expect(s.width(east_asian: false)).to be <= 10
|
87
89
|
expect(s).to eql('αβγδεζη...')
|
88
90
|
end
|
89
91
|
end
|
data/tool/benchmark.rb
CHANGED
@@ -6,13 +6,13 @@ require 'unicode/display_width'
|
|
6
6
|
|
7
7
|
str = "foo あいうえお bar αβγδε baz"
|
8
8
|
|
9
|
-
VisualWidth.
|
9
|
+
VisualWidth.measure(str) == str.display_width(2)
|
10
10
|
|
11
11
|
[str, str * 5, str * 10].each do |str|
|
12
|
-
puts "\n", "
|
12
|
+
puts "\n", "measures string with length #{str.length}:"
|
13
13
|
Benchmark.bmbm do |x|
|
14
14
|
x.report("visual_width") do
|
15
|
-
1000.times { VisualWidth.
|
15
|
+
1000.times { VisualWidth.measure(str) }
|
16
16
|
end
|
17
17
|
x.report("display_width") do
|
18
18
|
1000.times { str.display_width(2) }
|
data/tool/tables.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!ruby
|
2
|
+
require 'terminal-table'
|
3
|
+
require 'text-table'
|
4
|
+
require 'visual_width/table'
|
5
|
+
|
6
|
+
table = Text::Table.new
|
7
|
+
table.head = ['じゃがいも', 'さといも']
|
8
|
+
table.rows = [['コロッケ', '雑煮']]
|
9
|
+
table.rows << ['肉じゃが', '塩ゆで']
|
10
|
+
puts table
|
11
|
+
|
12
|
+
rows = []
|
13
|
+
rows << ['じゃがいも', 'さといも']
|
14
|
+
rows << ['コロッケ', '雑煮']
|
15
|
+
rows << ['肉じゃが', '塩ゆで']
|
16
|
+
table = Terminal::Table.new :rows => rows
|
17
|
+
puts table
|
18
|
+
|
19
|
+
t = VisualWidth::Table.new
|
20
|
+
|
21
|
+
puts t.draw(rows)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: visual_width
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fuji, Goro (gfx)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -47,20 +47,25 @@ extra_rdoc_files: []
|
|
47
47
|
files:
|
48
48
|
- .gitignore
|
49
49
|
- .rspec
|
50
|
+
- .travis.yml
|
50
51
|
- Changes
|
51
52
|
- Gemfile
|
52
53
|
- LICENSE.txt
|
53
54
|
- README.md
|
54
55
|
- Rakefile
|
56
|
+
- example/table.rb
|
55
57
|
- lib/visual_width.rb
|
56
58
|
- lib/visual_width/data.rb
|
57
59
|
- lib/visual_width/string_ext.rb
|
58
60
|
- lib/visual_width/string_refine.rb
|
61
|
+
- lib/visual_width/table.rb
|
59
62
|
- lib/visual_width/version.rb
|
60
63
|
- spec/visual_width/string_refine_spec.rb
|
64
|
+
- spec/visual_width/table_spec.rb
|
61
65
|
- spec/visual_width_spec.rb
|
62
66
|
- tool/benchmark.rb
|
63
67
|
- tool/east-asian-width.pl
|
68
|
+
- tool/tables.rb
|
64
69
|
- visual_width.gemspec
|
65
70
|
homepage: https://github.com/gfx/visual_width.rb
|
66
71
|
licenses:
|
@@ -88,5 +93,6 @@ specification_version: 4
|
|
88
93
|
summary: Ruby Implementation of East Asian Width
|
89
94
|
test_files:
|
90
95
|
- spec/visual_width/string_refine_spec.rb
|
96
|
+
- spec/visual_width/table_spec.rb
|
91
97
|
- spec/visual_width_spec.rb
|
92
98
|
has_rdoc:
|