visual_width 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|