visual_width 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4de483063c67cddbaca1f3ac3a1ac27536311218
4
- data.tar.gz: 3d9ab9b361e41e1edc9d2f095c28a3591e72cfbb
3
+ metadata.gz: 970d02ea37970eae029eb5078fd36eb85e662d9e
4
+ data.tar.gz: 619d3e14f123e4c9858b9328731209d8fe2f5efd
5
5
  SHA512:
6
- metadata.gz: fd041e918922cf48e5686b8d9d7c4417b8bbde49c11fa65f6f2d3c6783f0afccd6f41397d2aae6b75f94ed3cdba3e3fbdd04901433df06ee1ee935ed724b68e0
7
- data.tar.gz: 40a336d2d2e0e2b919109744910a17cb2860dfe13e59c69c7766c3707f65a614e8226402c1bbf5711953f81a154f1022c3c7e8df7bc93b76755d54fcd429f815
6
+ metadata.gz: 1c531f47569a9c8c4b616be2cf20d6f5bbc8235f61f07ae1e04f02f83863858e47efc73c44d73c5357c51711159c7d4158e2d3e09651ce95d9036e58402ca864
7
+ data.tar.gz: b2b573865cb4e4ec041f2bc12e5344ac01636dec1a3198273cd1dbf2ba5eb6c723216f436d7d5b72f0ff4e2b6c39677ca69699527a3949bb589343bdf1133cfb
data/.rspec CHANGED
@@ -1,2 +1 @@
1
1
  --color
2
- --format doc
data/Changes CHANGED
@@ -1,4 +1,7 @@
1
1
  This is a revision history of visual_width gem.
2
2
 
3
+ 0.0.2 2013-10-26 08:51:02+0900
4
+ - Added visual_width/table.rb to draw text tables
5
+
3
6
  0.0.1 2013-10-24 00:22:53+0900
4
7
  - This is the initial release of visual_width
data/README.md CHANGED
@@ -20,35 +20,48 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- ```ruby
24
- # VisualWidth
25
- require 'visual_width'
23
+ ## `visual_width`:
26
24
 
25
+ ```ruby
27
26
  p VisualWidth.measure("こんにちは") # => 10
28
27
  p VisualWidth.measure("abcdefghij") # => 10
29
28
 
30
29
  p VisualWidth.truncate("恋すてふ 我が名はまだき 立ちにけり 人知れずこそ 思ひそめしか", 20) # => "恋すてふ 我が名は..."
30
+ ```
31
+
32
+ `.measure()` and `truncate()` methods takes `east_asian: false` option to tell it is not in an East Asian context, regarding ambiguous characters as half-width.
33
+ See [Ambiguous Characters](http://www.unicode.org/reports/tr11/#Ambiguous) in the report.
31
34
 
32
- # VisualWidth::Table
35
+ ## `visual_width/table`:
36
+
37
+ ```ruby
33
38
  require 'visual_width/table'
34
39
 
35
40
  t = VisualWidth::Table.new(
36
- format: [VisualWidth::Table::LEFT, VisualWidth::Table::RIGHT, VisualWidth::Table::RIGHT]
41
+ style: [
42
+ { align: :center, width: 8 },
43
+ { align: :center, width: 8 },
44
+ { align: :right, width: 5 },
45
+ ],
37
46
  )
38
47
 
39
- header = ['Student', 'Mid-Terms', 'Finals']
48
+ header = ['Nick', 'FullName', 'Age']
40
49
  rows = [
41
- ['Sam', 94, 93],
42
- ['Jane', 92, 99],
43
- ['Average', 93, 96],
50
+ ['カネダ', '金田 正太郎', 17],
51
+ ['テツオ', '島 鉄雄', 16],
52
+ ['ケイ', '?', 18],
44
53
  ]
45
- t.draw(rows, header: header)
54
+ puts t.render(rows, header: header)
55
+ # +--------+--------+-----+
56
+ # | Nick |FullName| Age |
57
+ # +--------+--------+-----+
58
+ # | カネダ |金田 正 | 17|
59
+ # | | 太郎 | |
60
+ # | テツオ |島 鉄雄 | 16|
61
+ # | ケイ | ? | 18|
62
+ # +--------+--------+-----+
46
63
  ```
47
64
 
48
- Each method can take `east_asian: false` to tell it is not in an East Asian context, regarding ambiguous characters as half-width.
49
-
50
- See [Ambiguous Characters](http://www.unicode.org/reports/tr11/#Ambiguous) in the report.
51
-
52
65
  ## Contributing
53
66
 
54
67
  1. Fork it
@@ -60,3 +73,4 @@ See [Ambiguous Characters](http://www.unicode.org/reports/tr11/#Ambiguous) in th
60
73
  ## See Also
61
74
 
62
75
  * [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()`
76
+ * [terminal-table](https://rubygems.org/gems/terminal-table) renders text table, but it cannot deal with East Asian Width characters
data/example/table.rb CHANGED
@@ -1,13 +1,17 @@
1
1
  # VisualWidth::Table
2
2
  require 'visual_width/table'
3
3
  t = VisualWidth::Table.new(
4
- format: [VisualWidth::Table::LEFT, VisualWidth::Table::RIGHT, VisualWidth::Table::RIGHT]
4
+ style: [
5
+ { align: :center, width: 8 },
6
+ { align: :center, width: 8 },
7
+ { align: :right, width: 5 },
8
+ ],
5
9
  )
6
10
 
7
- header = ['Student', 'Mid-Terms', 'Finals']
11
+ header = ['Nick', 'FullName', 'Age']
8
12
  rows = [
9
- ['Sam', 94, 93],
10
- ['Jane', 92, 99],
11
- ['Average', 93, 96],
13
+ ['カネダ', '金田 正太郎', 17],
14
+ ['テツオ', '島 鉄雄', 16],
15
+ ['ケイ', '?', 18],
12
16
  ]
13
- puts t.draw(rows, header: header)
17
+ puts t.render(rows, header: header)
data/lib/visual_width.rb CHANGED
@@ -29,25 +29,36 @@ module VisualWidth
29
29
  (full_width * 2) + (str.length - full_width)
30
30
  end
31
31
 
32
- def truncate(str, max_length, omission: '...', east_asian: EAST_ASIAN)
32
+ def each_width(str, max_width, east_asian: EAST_ASIAN) # requires block
33
33
  rx = east_asian ? @@t1 : @@t0
34
- max = max_length - omission.length
35
34
  pos = 0
36
35
  width = 0
37
36
  str.scan(rx) do |wide,|
38
37
  width += wide ? 2 : 1
39
-
40
- break if width > max
41
-
42
38
  pos += 1
43
39
 
44
- break if width == max
40
+ next_char = str[pos]
41
+ next_char_width = next_char ? measure(next_char, east_asian: east_asian) : 0
42
+ if (width + next_char_width) > max_width
43
+ yield str.slice(0, pos)
44
+ str = str.slice(pos, str.length)
45
+ pos = 0
46
+ width = 0
47
+ end
45
48
  end
49
+ if str.length > 0
50
+ yield str
51
+ end
52
+ end
46
53
 
47
- if width < str.length
48
- str.slice(0, pos) + omission
49
- else
50
- str
54
+ def truncate(str, max_length, omission: '...', east_asian: EAST_ASIAN)
55
+ max = max_length - omission.length
56
+ each_width(str, max, east_asian: east_asian) do |line|
57
+ if line.length == str.length
58
+ return line
59
+ else
60
+ return line + omission
61
+ end
51
62
  end
52
63
  end
53
64
  end
@@ -0,0 +1,34 @@
1
+ require 'visual_width'
2
+
3
+ module VisualWidth::Formatter
4
+ class Align
5
+ def left(cell, width)
6
+ align(cell, width) do |line, fill|
7
+ line + (' ' * fill)
8
+ end
9
+ end
10
+
11
+ def right(cell, width)
12
+ align(cell, width) do |line, fill|
13
+ (' ' * fill) + line
14
+ end
15
+ end
16
+
17
+ def center(cell, width)
18
+ align(cell, width) do |line, fill|
19
+ half = fill / 2.0
20
+ (' ' * half.floor) + line + (' ' * half.ceil)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def align(cell, width)
27
+ w = VisualWidth.measure(cell)
28
+ if w > width
29
+ raise ArgumentError, "Invalid width cell: #{cell.inspect}, width: #{width.inspect}"
30
+ end
31
+ yield cell, width - w
32
+ end
33
+ end
34
+ end
@@ -1,83 +1,130 @@
1
1
  require 'visual_width'
2
+ require 'visual_width/formatter'
2
3
 
3
4
  class VisualWidth::Table
4
- attr_accessor :header, :footer, :format
5
+ include VisualWidth
5
6
 
6
- # align (left, right, center)
7
- LEFT = -> (cell, fill) {
8
- cell + (' ' * fill)
9
- }
7
+ attr_accessor :header, :style
10
8
 
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: [])
9
+ def initialize(header: nil, style: [])
21
10
  @header = header
22
- @format = format
11
+ @style = style
12
+
13
+ @needs_wrap = @style.any? { |style| style[:width] != nil }
23
14
  end
24
15
 
25
- def draw(rows, header: nil, output: "")
16
+ def render(rows, header: nil, output: "")
17
+ if @needs_wrap
18
+ default_style = {}
19
+ rows = rows.map do |row|
20
+ i = 0
21
+ row.map do |cell|
22
+ cell = "#{cell}"
23
+ width = (@style[i] || default_style)[:width]
24
+ i += 1
25
+ if width
26
+ wrap(cell, width)
27
+ else
28
+ cell
29
+ end
30
+ end
31
+ end
32
+ end
26
33
  max_widths = calc_max_widths(rows)
27
34
  h = header || @header
35
+ style_header = []
28
36
  if h
29
- max_widths = h.map { |cell| VisualWidth.measure(cell) }
37
+ max_widths = calc_max_widths([h])
30
38
  .zip(max_widths)
31
39
  .map { |values| values.max }
32
- format_header = [CENTER] * h.length
33
- else
34
- format_header = nil
40
+ h.length.times do |i|
41
+ style = @style[i] || {}
42
+
43
+ style_header << style.merge(align: :center)
44
+ end
35
45
  end
36
- draw_row(output, max_widths, format_header, h, separated: true)
46
+ draw_header(output, max_widths, style_header, h)
37
47
  rows.each do |row|
38
- draw_row(output, max_widths, @format, row)
48
+ draw_row(output, max_widths, @style, row)
39
49
  end
40
50
  line(output, max_widths)
41
51
  output
42
52
  end
53
+
54
+ def draw(*args)
55
+ warn "draw() is deprecated. Use render() instead."
56
+ render(*args)
57
+ end
58
+
43
59
  private
44
60
 
45
- def draw_row(output, max_widths, format, row, separated: false)
46
- if separated
61
+ def draw_header(output, max_widths, style, row)
62
+ line(output, max_widths)
63
+
64
+ if row && row.length > 0
65
+ draw_row(output, max_widths, style, row)
47
66
  line(output, max_widths)
48
67
  end
68
+ end
49
69
 
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"
70
+ def draw_row(output, max_widths, style, row)
71
+ output << '|'
57
72
 
58
- if separated
59
- line(output, max_widths)
73
+ rows = []
74
+ max_widths.length.times do |i|
75
+ cell = "#{row[i]}"
76
+ s = style[i] || {}
77
+ align = s[:align] || :left
78
+ width = s[:width] || max_widths[i]
79
+ c = cell.split(/\n/)
80
+ if c.length == 0
81
+ c << ""
60
82
  end
83
+ output << aligner.send(align, c.shift.strip, width) << '|'
84
+ if c.length > 0
85
+ c.each_with_index do |new_cell, row_id|
86
+ rows[row_id] ||= []
87
+ rows[row_id][i] = new_cell
88
+ end
89
+ end
90
+ end
91
+ output << "\n"
92
+
93
+ rows.each do |row|
94
+ draw_row(output, max_widths, style, row)
61
95
  end
96
+
97
+ output
62
98
  end
63
99
 
64
- def line (output, max_widths)
65
- output << '+' << max_widths.map { |width| '-' * width }.join('+') << "+\n"
100
+ def line (output, widths)
101
+ output << '+' << widths.map { |width| '-' * width }.join('+') << "+\n"
66
102
  end
67
103
 
68
- def fill(output, max_width, cell, f)
69
- f ||= LEFT
70
- w = VisualWidth.measure(cell)
71
- output << f.call(cell, (max_width - w))
104
+ def aligner
105
+ VisualWidth::Formatter::Align.new
72
106
  end
73
107
 
74
108
  def calc_max_widths(rows) # -> [max_col0_width, max_col1_width, ...]
75
109
  result = []
110
+ default_style = {}
76
111
  rows.each_with_index do |row|
77
112
  row.each_with_index do |cell, i|
78
- result[i] = [result[i] || 0, VisualWidth.measure(cell.to_s)].max
113
+ ws = "#{cell}".split(/\n/).map do |line|
114
+ measure(line)
115
+ end
116
+ style = @style[i] || default_style
117
+ result[i] = (ws << (result[i] || 0) << (style[:width] || 0)).max
79
118
  end
80
119
  end
81
120
  result
82
121
  end
122
+
123
+ def wrap(cell, width)
124
+ s = ""
125
+ each_width(cell.strip, width) do |line|
126
+ s << line.strip << "\n"
127
+ end
128
+ s
129
+ end
83
130
  end
@@ -1,3 +1,3 @@
1
1
  module VisualWidth
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -13,11 +13,11 @@ describe VisualWidth::Table do
13
13
  rows
14
14
  end
15
15
 
16
- context "#draw" do
17
- it 'draws text table' do
16
+ context "#render" do
17
+ it 'renders text table' do
18
18
  t = VisualWidth::Table.new
19
19
 
20
- expect(t.draw(rows)).to eql(<<-'TEXT')
20
+ expect(t.render(rows)).to eql(<<-'TEXT')
21
21
  +----------+----------+
22
22
  |りんご |なし |
23
23
  |べにしぐれ|せいぎょく|
@@ -26,12 +26,12 @@ describe VisualWidth::Table do
26
26
  TEXT
27
27
  end
28
28
 
29
- it 'draws text table with formatter/CENTER, RIGHT' do
29
+ it 'renders text table with style/CENTER, RIGHT' do
30
30
  t = VisualWidth::Table.new(
31
- format: [VisualWidth::Table::CENTER, VisualWidth::Table::RIGHT],
31
+ style: [{align: :center}, {align: :right}],
32
32
  )
33
33
 
34
- expect(t.draw(rows)).to eql(<<-'TEXT')
34
+ expect(t.render(rows)).to eql(<<-'TEXT')
35
35
  +----------+----------+
36
36
  | りんご | なし|
37
37
  |べにしぐれ|せいぎょく|
@@ -40,11 +40,11 @@ describe VisualWidth::Table do
40
40
  TEXT
41
41
  end
42
42
 
43
- it 'draws text table with header/footer' do
43
+ it 'renders text table with header/footer' do
44
44
  t = VisualWidth::Table.new(
45
45
  header: %w(A B),
46
46
  )
47
- expect(t.draw(rows)).to eql(<<-'TEXT')
47
+ expect(t.render(rows)).to eql(<<-'TEXT')
48
48
  +----------+----------+
49
49
  | A | B |
50
50
  +----------+----------+
@@ -63,7 +63,7 @@ describe VisualWidth::Table do
63
63
  ['Average', 93, 96],
64
64
  ]
65
65
 
66
- expect(VisualWidth::Table.new.draw(rows, header: header)).to eql(<<-'TEXT')
66
+ expect(VisualWidth::Table.new.render(rows, header: header)).to eql(<<-'TEXT')
67
67
  +-------+---------+------+
68
68
  |Student|Mid-Terms|Finals|
69
69
  +-------+---------+------+
@@ -74,4 +74,16 @@ describe VisualWidth::Table do
74
74
  TEXT
75
75
  end
76
76
  end
77
+
78
+ context "#render with wrap" do
79
+ it "wraps with 10 width" do
80
+ t = VisualWidth::Table.new(style: [{width: 10}, {width: 10}])
81
+ expect(t.render([['テレポーター!', '*いしのなかにいる*']])).to eql(<<-'TEXT')
82
+ +----------+----------+
83
+ |テレポータ|*いしのな |
84
+ |ー! |かにいる* |
85
+ +----------+----------+
86
+ TEXT
87
+ end
88
+ end
77
89
  end
@@ -89,4 +89,24 @@ describe VisualWidth do
89
89
  expect(s).to eql('αβγδεζη...')
90
90
  end
91
91
  end
92
+
93
+ context ".each_width" do
94
+ it "splits str in visual width" do
95
+ str = "恋すてふ_我が名はまだき_立ちにけり_人知れずこそ_思ひそめしか"
96
+
97
+ values = []
98
+ VisualWidth.each_width(str, 10) do |line|
99
+ values << line
100
+ end
101
+ expect(values).to eql(%w(
102
+ 恋すてふ_
103
+ 我が名はま
104
+ だき_立ち
105
+ にけり_人
106
+ 知れずこそ
107
+ _思ひそめ
108
+ しか
109
+ ))
110
+ end
111
+ end
92
112
  end
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.2
4
+ version: 0.0.3
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-24 00:00:00.000000000 Z
11
+ date: 2013-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -56,6 +56,7 @@ files:
56
56
  - example/table.rb
57
57
  - lib/visual_width.rb
58
58
  - lib/visual_width/data.rb
59
+ - lib/visual_width/formatter.rb
59
60
  - lib/visual_width/string_ext.rb
60
61
  - lib/visual_width/string_refine.rb
61
62
  - lib/visual_width/table.rb